diff options
301 files changed, 6588 insertions, 2233 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 5d9f3357125a..a6f47d4e4908 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -109,6 +109,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseLongArray; +import android.util.SparseSetArray; import android.util.TimeUtils; import android.view.Display; import android.widget.Toast; @@ -452,6 +453,12 @@ public class AppStandbyController private final Map<String, String> mAppStandbyProperties = new ArrayMap<>(); /** + * Set of apps that were restored via backup & restore, per user, that need their + * standby buckets to be adjusted when installed. + */ + private final SparseSetArray<String> mAppsToRestoreToRare = new SparseSetArray<>(); + + /** * List of app-ids of system packages, populated on boot, when system services are ready. */ private final ArrayList<Integer> mSystemPackagesAppIds = new ArrayList<>(); @@ -968,13 +975,17 @@ public class AppStandbyController + standbyBucketToString(newBucket)); } } else { - newBucket = getBucketForLocked(packageName, userId, - elapsedRealtime); - if (DEBUG) { - Slog.d(TAG, "Evaluated AOSP newBucket = " - + standbyBucketToString(newBucket)); + // Don't update the standby state for apps that were restored + if (!(oldMainReason == REASON_MAIN_DEFAULT + && (app.bucketingReason & REASON_SUB_MASK) + == REASON_SUB_DEFAULT_APP_RESTORED)) { + newBucket = getBucketForLocked(packageName, userId, elapsedRealtime); + if (DEBUG) { + Slog.d(TAG, "Evaluated AOSP newBucket = " + + standbyBucketToString(newBucket)); + } + reason = REASON_MAIN_TIMEOUT; } - reason = REASON_MAIN_TIMEOUT; } } @@ -1610,18 +1621,29 @@ public class AppStandbyController final int reason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_RESTORED; final long nowElapsed = mInjector.elapsedRealtime(); for (String packageName : restoredApps) { - // If the package is not installed, don't allow the bucket to be set. + // If the package is not installed, don't allow the bucket to be set. Instead, add it + // to a list of all packages whose buckets need to be adjusted when installed. if (!mInjector.isPackageInstalled(packageName, 0, userId)) { - Slog.e(TAG, "Tried to restore bucket for uninstalled app: " + packageName); + Slog.i(TAG, "Tried to restore bucket for uninstalled app: " + packageName); + mAppsToRestoreToRare.add(userId, packageName); continue; } - final int standbyBucket = getAppStandbyBucket(packageName, userId, nowElapsed, false); - // Only update the standby bucket to RARE if the app is still in the NEVER bucket. - if (standbyBucket == STANDBY_BUCKET_NEVER) { - setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RARE, reason, - nowElapsed, false); - } + restoreAppToRare(packageName, userId, nowElapsed, reason); + } + // Clear out the list of restored apps that need to have their standby buckets adjusted + // if they still haven't been installed eight hours after restore. + // Note: if the device reboots within these first 8 hours, this list will be lost since it's + // not persisted - this is the expected behavior for now and may be updated in the future. + mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), 8 * ONE_HOUR); + } + + /** Adjust the standby bucket of the given package for the user to RARE. */ + private void restoreAppToRare(String pkgName, int userId, long nowElapsed, int reason) { + final int standbyBucket = getAppStandbyBucket(pkgName, userId, nowElapsed, false); + // Only update the standby bucket to RARE if the app is still in the NEVER bucket. + if (standbyBucket == STANDBY_BUCKET_NEVER) { + setAppStandbyBucket(pkgName, userId, STANDBY_BUCKET_RARE, reason, nowElapsed, false); } } @@ -2116,15 +2138,24 @@ public class AppStandbyController } // component-level enable/disable can affect bucketing, so we always // reevaluate that for any PACKAGE_CHANGED - mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkgName) - .sendToTarget(); + if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { + mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkgName) + .sendToTarget(); + } } if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) || Intent.ACTION_PACKAGE_ADDED.equals(action))) { if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { maybeUnrestrictBuggyApp(pkgName, userId); - } else { + } else if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) { clearAppIdleForPackage(pkgName, userId); + } else { + // Package was just added and it's not being replaced. + if (mAppsToRestoreToRare.contains(userId, pkgName)) { + restoreAppToRare(pkgName, userId, mInjector.elapsedRealtime(), + REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_RESTORED); + mAppsToRestoreToRare.remove(userId, pkgName); + } } } } diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java index 19606c16cb70..df8a50a1fa5c 100644 --- a/core/java/android/animation/AnimationHandler.java +++ b/core/java/android/animation/AnimationHandler.java @@ -19,10 +19,10 @@ package android.animation; import android.os.SystemClock; import android.os.SystemProperties; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import android.view.Choreographer; +import java.lang.ref.WeakReference; import java.util.ArrayList; /** @@ -40,7 +40,7 @@ import java.util.ArrayList; public class AnimationHandler { private static final String TAG = "AnimationHandler"; - private static final boolean LOCAL_LOGV = true; + private static final boolean LOCAL_LOGV = false; /** * Internal per-thread collections used to avoid set collisions as animations start and end @@ -78,7 +78,7 @@ public class AnimationHandler { * store visible (foreground) requestors; if the set size reaches zero, there are no * objects in the foreground and it is time to pause animators. */ - private final ArraySet<Object> mAnimatorRequestors = new ArraySet<>(); + private final ArrayList<WeakReference<Object>> mAnimatorRequestors = new ArrayList<>(); private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override @@ -141,24 +141,9 @@ public class AnimationHandler { * tracking obsolete+enabled requestors. */ public static void removeRequestor(Object requestor) { - getInstance().removeRequestorImpl(requestor); - } - - private void removeRequestorImpl(Object requestor) { - // Also request disablement, in case that requestor was the sole object keeping - // animators un-paused - long startTime = System.nanoTime(); - requestAnimatorsEnabled(false, requestor); - Log.d(TAG, "removeRequestorImpl called requestAnimatorsEnabled after " + - (System.nanoTime() - startTime)); - mAnimatorRequestors.remove(requestor); - Log.d(TAG, "removeRequestorImpl removed requestor after " + - (System.nanoTime() - startTime)); + getInstance().requestAnimatorsEnabledImpl(false, requestor); if (LOCAL_LOGV) { - Log.v(TAG, "removeRequestorImpl for " + requestor); - for (int i = 0; i < mAnimatorRequestors.size(); ++i) { - Log.v(TAG, "animatorRequesters " + i + " = " + mAnimatorRequestors.valueAt(i)); - } + Log.v(TAG, "removeRequestor for " + requestor); } } @@ -176,25 +161,44 @@ public class AnimationHandler { } private void requestAnimatorsEnabledImpl(boolean enable, Object requestor) { - long startTime = System.nanoTime(); boolean wasEmpty = mAnimatorRequestors.isEmpty(); setAnimatorPausingEnabled(isPauseBgAnimationsEnabledInSystemProperties()); - Log.d(TAG, "requestAnimatorsEnabledImpl called setAnimatorPausingEnabled after " + - (System.nanoTime() - startTime)); - if (enable) { - mAnimatorRequestors.add(requestor); - } else { - mAnimatorRequestors.remove(requestor); + synchronized (mAnimatorRequestors) { + // Only store WeakRef objects to avoid leaks + if (enable) { + // First, check whether such a reference is already on the list + WeakReference<Object> weakRef = null; + for (int i = mAnimatorRequestors.size() - 1; i >= 0; --i) { + WeakReference<Object> ref = mAnimatorRequestors.get(i); + Object referent = ref.get(); + if (referent == requestor) { + weakRef = ref; + } else if (referent == null) { + // Remove any reference that has been cleared + mAnimatorRequestors.remove(i); + } + } + if (weakRef == null) { + weakRef = new WeakReference<>(requestor); + mAnimatorRequestors.add(weakRef); + } + } else { + for (int i = mAnimatorRequestors.size() - 1; i >= 0; --i) { + WeakReference<Object> ref = mAnimatorRequestors.get(i); + Object referent = ref.get(); + if (referent == requestor || referent == null) { + // remove requested item or item that has been cleared + mAnimatorRequestors.remove(i); + } + } + // If a reference to the requestor wasn't in the list, nothing to remove + } } - Log.d(TAG, "requestAnimatorsEnabledImpl added/removed after " + - (System.nanoTime() - startTime)); if (!sAnimatorPausingEnabled) { // Resume any animators that have been paused in the meantime, otherwise noop // Leave logic above so that if pausing gets re-enabled, the state of the requestors // list is valid resumeAnimators(); - Log.d(TAG, "requestAnimatorsEnabledImpl resumed, returning after " + - (System.nanoTime() - startTime)); return; } boolean isEmpty = mAnimatorRequestors.isEmpty(); @@ -209,12 +213,13 @@ public class AnimationHandler { Animator.getBackgroundPauseDelay()); } } - Log.d(TAG, "requestAnimatorsEnabledImpl post was/is check after " + - (System.nanoTime() - startTime)); if (LOCAL_LOGV) { - Log.v(TAG, enable ? "enable" : "disable" + " animators for " + requestor); + Log.v(TAG, (enable ? "enable" : "disable") + " animators for " + requestor + + " with pauseDelay of " + Animator.getBackgroundPauseDelay()); for (int i = 0; i < mAnimatorRequestors.size(); ++i) { - Log.v(TAG, "animatorRequesters " + i + " = " + mAnimatorRequestors.valueAt(i)); + Log.v(TAG, "animatorRequestors " + i + " = " + + mAnimatorRequestors.get(i) + " with referent " + + mAnimatorRequestors.get(i).get()); } } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 13fc2e6977ea..3c0a724b9ff7 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -511,10 +511,26 @@ public class StatusBarManager { @SystemApi public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; + /** + * State indicating that media transfer to this receiver device is succeeded. + * + * @hide + */ + public static final int MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 2; + + /** + * State indicating that media transfer to this receiver device is failed. + * + * @hide + */ + public static final int MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED = 3; + /** @hide */ @IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = { MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED, }) @Retention(RetentionPolicy.SOURCE) public @interface MediaTransferReceiverState {} diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl index 6b9d2c73450e..e4ac01195bcb 100644 --- a/core/java/android/app/trust/ITrustListener.aidl +++ b/core/java/android/app/trust/ITrustListener.aidl @@ -24,8 +24,8 @@ import java.util.List; * {@hide} */ oneway interface ITrustListener { - void onTrustChanged(boolean enabled, int userId, int flags, + void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags, in List<String> trustGrantedMessages); void onTrustManagedChanged(boolean managed, int userId); void onTrustError(in CharSequence message); -}
\ No newline at end of file +} diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 9e825b7207e0..62f755d0268c 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -22,6 +22,7 @@ import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.hardware.biometrics.BiometricSourceType; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -45,6 +46,7 @@ public class TrustManager { private static final String TAG = "TrustManager"; private static final String DATA_FLAGS = "initiatedByUser"; + private static final String DATA_NEWLY_UNLOCKED = "newlyUnlocked"; private static final String DATA_MESSAGE = "message"; private static final String DATA_GRANTED_MESSAGES = "grantedMessages"; @@ -171,13 +173,14 @@ public class TrustManager { try { ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { @Override - public void onTrustChanged(boolean enabled, int userId, int flags, - List<String> trustGrantedMessages) { + public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, + int flags, List<String> trustGrantedMessages) { Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId, trustListener); if (flags != 0) { m.getData().putInt(DATA_FLAGS, flags); } + m.getData().putInt(DATA_NEWLY_UNLOCKED, newlyUnlocked ? 1 : 0); m.getData().putCharSequenceArrayList( DATA_GRANTED_MESSAGES, (ArrayList) trustGrantedMessages); m.sendToTarget(); @@ -265,9 +268,14 @@ public class TrustManager { public void handleMessage(Message msg) { switch(msg.what) { case MSG_TRUST_CHANGED: - int flags = msg.peekData() != null ? msg.peekData().getInt(DATA_FLAGS) : 0; - ((TrustListener) msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags, - msg.getData().getStringArrayList(DATA_GRANTED_MESSAGES)); + Bundle data = msg.peekData(); + int flags = data != null ? data.getInt(DATA_FLAGS) : 0; + boolean enabled = msg.arg1 != 0; + int newlyUnlockedInt = + data != null ? data.getInt(DATA_NEWLY_UNLOCKED) : 0; + boolean newlyUnlocked = newlyUnlockedInt != 0; + ((TrustListener) msg.obj).onTrustChanged(enabled, newlyUnlocked, msg.arg2, + flags, msg.getData().getStringArrayList(DATA_GRANTED_MESSAGES)); break; case MSG_TRUST_MANAGED_CHANGED: ((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2); @@ -284,6 +292,8 @@ public class TrustManager { /** * Reports that the trust state has changed. * @param enabled If true, the system believes the environment to be trusted. + * @param newlyUnlocked If true, the system believes the device is newly unlocked due + * to the trust changing. * @param userId The user, for which the trust changed. * @param flags Flags specified by the trust agent when granting trust. See * {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int) @@ -291,7 +301,7 @@ public class TrustManager { * @param trustGrantedMessages Messages to display to the user when trust has been granted * by one or more trust agents. */ - void onTrustChanged(boolean enabled, int userId, int flags, + void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags, List<String> trustGrantedMessages); /** diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index b0b3e9e743cf..ff80ffdadff0 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -427,9 +427,6 @@ public abstract class DisplayManagerInternal { // 1 (brighter). Set to Float.NaN if there's no override. public float screenAutoBrightnessAdjustmentOverride; - // If true, enables automatic brightness control. - public boolean useAutoBrightness; - // If true, scales the brightness to a fraction of desired (as defined by // screenLowPowerBrightnessFactor). public boolean lowPowerMode; @@ -459,7 +456,6 @@ public abstract class DisplayManagerInternal { policy = POLICY_BRIGHT; useProximitySensor = false; screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; - useAutoBrightness = false; screenAutoBrightnessAdjustmentOverride = Float.NaN; screenLowPowerBrightnessFactor = 0.5f; blockScreenOn = false; @@ -483,7 +479,6 @@ public abstract class DisplayManagerInternal { policy = other.policy; useProximitySensor = other.useProximitySensor; screenBrightnessOverride = other.screenBrightnessOverride; - useAutoBrightness = other.useAutoBrightness; screenAutoBrightnessAdjustmentOverride = other.screenAutoBrightnessAdjustmentOverride; screenLowPowerBrightnessFactor = other.screenLowPowerBrightnessFactor; blockScreenOn = other.blockScreenOn; @@ -505,7 +500,6 @@ public abstract class DisplayManagerInternal { && useProximitySensor == other.useProximitySensor && floatEquals(screenBrightnessOverride, other.screenBrightnessOverride) - && useAutoBrightness == other.useAutoBrightness && floatEquals(screenAutoBrightnessAdjustmentOverride, other.screenAutoBrightnessAdjustmentOverride) && screenLowPowerBrightnessFactor @@ -531,7 +525,6 @@ public abstract class DisplayManagerInternal { return "policy=" + policyToString(policy) + ", useProximitySensor=" + useProximitySensor + ", screenBrightnessOverride=" + screenBrightnessOverride - + ", useAutoBrightness=" + useAutoBrightness + ", screenAutoBrightnessAdjustmentOverride=" + screenAutoBrightnessAdjustmentOverride + ", screenLowPowerBrightnessFactor=" + screenLowPowerBrightnessFactor diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index a46868e93ab8..68e1acb374d2 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -523,6 +523,13 @@ public final class PowerManager { public static final int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0; /** + * Go to sleep flag: Sleep softly, go to sleep only if there's no wakelock explicitly keeping + * the device awake. + * @hide + */ + public static final int GO_TO_SLEEP_FLAG_SOFT_SLEEP = 1 << 1; + + /** * @hide */ @IntDef(prefix = { "BRIGHTNESS_CONSTRAINT_TYPE" }, value = { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d6fa02c6ff5b..94a6382227f3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9514,6 +9514,16 @@ public final class Settings { "lock_screen_show_silent_notifications"; /** + * Indicates whether "seen" notifications should be suppressed from the lockscreen. + * <p> + * Type: int (0 for false, 1 for true) + * + * @hide + */ + public static final String LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS = + "lock_screen_show_only_unseen_notifications"; + + /** * Indicates whether snooze options should be shown on notifications * <p> * Type: int (0 for false, 1 for true) diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 1507c87c0452..6e6dac5766e9 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -111,6 +111,12 @@ public final class FillRequest implements Parcelable { */ public static final @RequestFlags int FLAG_IME_SHOWING = 0x80; + /** + * Indicates whether autofill session should reset the fill dialog state. + * @hide + */ + public static final @RequestFlags int FLAG_RESET_FILL_DIALOG_STATE = 0x100; + /** @hide */ public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; @@ -208,7 +214,8 @@ public final class FillRequest implements Parcelable { FLAG_PASSWORD_INPUT_TYPE, FLAG_VIEW_NOT_FOCUSED, FLAG_SUPPORTS_FILL_DIALOG, - FLAG_IME_SHOWING + FLAG_IME_SHOWING, + FLAG_RESET_FILL_DIALOG_STATE }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -236,6 +243,8 @@ public final class FillRequest implements Parcelable { return "FLAG_SUPPORTS_FILL_DIALOG"; case FLAG_IME_SHOWING: return "FLAG_IME_SHOWING"; + case FLAG_RESET_FILL_DIALOG_STATE: + return "FLAG_RESET_FILL_DIALOG_STATE"; default: return Integer.toHexString(value); } } @@ -312,7 +321,8 @@ public final class FillRequest implements Parcelable { | FLAG_PASSWORD_INPUT_TYPE | FLAG_VIEW_NOT_FOCUSED | FLAG_SUPPORTS_FILL_DIALOG - | FLAG_IME_SHOWING); + | FLAG_IME_SHOWING + | FLAG_RESET_FILL_DIALOG_STATE); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; this.mDelayedFillIntentSender = delayedFillIntentSender; @@ -473,7 +483,8 @@ public final class FillRequest implements Parcelable { | FLAG_PASSWORD_INPUT_TYPE | FLAG_VIEW_NOT_FOCUSED | FLAG_SUPPORTS_FILL_DIALOG - | FLAG_IME_SHOWING); + | FLAG_IME_SHOWING + | FLAG_RESET_FILL_DIALOG_STATE); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; this.mDelayedFillIntentSender = delayedFillIntentSender; @@ -495,10 +506,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1647856966565L, + time = 1663290803064L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index 9396a888ec13..950c8ac01ce5 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -78,7 +78,7 @@ public abstract class ControlsProviderService extends Service { * @hide */ public static final String EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS = - "android.service.controls.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS"; + "android.service.controls.extra.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS"; /** * @hide diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c0183ad48139..af070238787f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1417,7 +1417,11 @@ public final class ViewRootImpl implements ViewParent, mFirstPostImeInputStage = earlyPostImeStage; mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix; - AnimationHandler.requestAnimatorsEnabled(mAppVisible, this); + if (!mRemoved || !mAppVisible) { + AnimationHandler.requestAnimatorsEnabled(mAppVisible, this); + } else if (LOCAL_LOGV) { + Log.v(mTag, "setView() enabling visibility when removed"); + } } } } @@ -1755,7 +1759,12 @@ public final class ViewRootImpl implements ViewParent, if (!mAppVisible) { WindowManagerGlobal.trimForeground(); } - AnimationHandler.requestAnimatorsEnabled(mAppVisible, this); + // Only enable if the window is not already removed (via earlier call to doDie()) + if (!mRemoved || !mAppVisible) { + AnimationHandler.requestAnimatorsEnabled(mAppVisible, this); + } else if (LOCAL_LOGV) { + Log.v(mTag, "handleAppVisibility() enabling visibility when removed"); + } } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 7d7270d1ef76..79c3839a967e 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -19,6 +19,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_IME_SHOWING; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; +import static android.service.autofill.FillRequest.FLAG_RESET_FILL_DIALOG_STATE; import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.view.ContentInfo.SOURCE_AUTOFILL; @@ -715,7 +716,7 @@ public final class AutofillManager { * Autofill will automatically trigger a fill request after activity * start if there is any field is autofillable. But if there is a field that * triggered autofill, it is unnecessary to trigger again through - * AutofillManager#notifyViewEnteredForActivityStarted. + * AutofillManager#notifyViewEnteredForFillDialog. */ private AtomicBoolean mIsFillRequested; @@ -728,6 +729,10 @@ public final class AutofillManager { private final String[] mFillDialogEnabledHints; + // Tracked all views that have appeared, including views that there are no + // dataset in responses. Used to avoid request pre-fill request again and again. + private final ArraySet<AutofillId> mAllTrackedViews = new ArraySet<>(); + /** @hide */ public interface AutofillClient { /** @@ -1173,6 +1178,16 @@ public final class AutofillManager { * @hide */ public void notifyViewEnteredForFillDialog(View v) { + synchronized (mLock) { + if (mTrackedViews != null) { + // To support the fill dialog can show for the autofillable Views in + // different pages but in the same Activity. We need to reset the + // mIsFillRequested flag to allow asking for a new FillRequest when + // user switches to other page + mTrackedViews.checkViewState(v.getAutofillId()); + } + } + // Skip if the fill request has been performed for a view. if (mIsFillRequested.get()) { return; @@ -1299,6 +1314,10 @@ public final class AutofillManager { } mForAugmentedAutofillOnly = false; } + + if ((flags & FLAG_SUPPORTS_FILL_DIALOG) != 0) { + flags |= FLAG_RESET_FILL_DIALOG_STATE; + } updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); } addEnteredIdLocked(id); @@ -2165,6 +2184,7 @@ public final class AutofillManager { mIsFillRequested.set(false); mShowAutofillDialogCalled = false; mFillDialogTriggerIds = null; + mAllTrackedViews.clear(); if (resetEnteredIds) { mEnteredIds = null; } @@ -2702,14 +2722,9 @@ public final class AutofillManager { + ", mFillableIds=" + mFillableIds + ", mEnabled=" + mEnabled + ", mSessionId=" + mSessionId); - } + if (mEnabled && mSessionId == sessionId) { - if (saveOnAllViewsInvisible) { - mTrackedViews = new TrackedViews(trackedIds); - } else { - mTrackedViews = null; - } mSaveOnFinish = saveOnFinish; if (fillableIds != null) { if (mFillableIds == null) { @@ -2731,6 +2746,27 @@ public final class AutofillManager { mSaveTriggerId = saveTriggerId; setNotifyOnClickLocked(mSaveTriggerId, true); } + + if (!saveOnAllViewsInvisible) { + trackedIds = null; + } + + final ArraySet<AutofillId> allFillableIds = new ArraySet<>(); + if (mFillableIds != null) { + allFillableIds.addAll(mFillableIds); + } + if (trackedIds != null) { + for (AutofillId id : trackedIds) { + id.resetSessionId(); + allFillableIds.add(id); + } + } + + if (!allFillableIds.isEmpty()) { + mTrackedViews = new TrackedViews(trackedIds, Helper.toArray(allFillableIds)); + } else { + mTrackedViews = null; + } } } } @@ -3502,10 +3538,19 @@ public final class AutofillManager { */ private class TrackedViews { /** Visible tracked views */ - @Nullable private ArraySet<AutofillId> mVisibleTrackedIds; + @NonNull private final ArraySet<AutofillId> mVisibleTrackedIds; /** Invisible tracked views */ - @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; + @NonNull private final ArraySet<AutofillId> mInvisibleTrackedIds; + + /** Visible tracked views for fill dialog */ + @NonNull private final ArraySet<AutofillId> mVisibleDialogTrackedIds; + + /** Invisible tracked views for fill dialog */ + @NonNull private final ArraySet<AutofillId> mInvisibleDialogTrackedIds; + + boolean mHasNewTrackedView; + boolean mIsTrackedSaveView; /** * Check if set is null or value is in set. @@ -3571,43 +3616,65 @@ public final class AutofillManager { * * @param trackedIds The views to be tracked */ - TrackedViews(@Nullable AutofillId[] trackedIds) { - final AutofillClient client = getClient(); - if (!ArrayUtils.isEmpty(trackedIds) && client != null) { - final boolean[] isVisible; - - if (client.autofillClientIsVisibleForAutofill()) { - if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); - isVisible = client.autofillClientGetViewVisibility(trackedIds); - } else { - // All false - isVisible = new boolean[trackedIds.length]; - } - - final int numIds = trackedIds.length; - for (int i = 0; i < numIds; i++) { - final AutofillId id = trackedIds[i]; - id.resetSessionId(); + TrackedViews(@Nullable AutofillId[] trackedIds, @Nullable AutofillId[] allTrackedIds) { + mVisibleTrackedIds = new ArraySet<>(); + mInvisibleTrackedIds = new ArraySet<>(); + if (!ArrayUtils.isEmpty(trackedIds)) { + mIsTrackedSaveView = true; + initialTrackedViews(trackedIds, mVisibleTrackedIds, mInvisibleTrackedIds); + } - if (isVisible[i]) { - mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); - } else { - mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); - } - } + mVisibleDialogTrackedIds = new ArraySet<>(); + mInvisibleDialogTrackedIds = new ArraySet<>(); + if (!ArrayUtils.isEmpty(allTrackedIds)) { + initialTrackedViews(allTrackedIds, mVisibleDialogTrackedIds, + mInvisibleDialogTrackedIds); + mAllTrackedViews.addAll(Arrays.asList(allTrackedIds)); } if (sVerbose) { Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): " + " mVisibleTrackedIds=" + mVisibleTrackedIds - + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); + + " mInvisibleTrackedIds=" + mInvisibleTrackedIds + + " allTrackedIds=" + Arrays.toString(allTrackedIds) + + " mVisibleDialogTrackedIds=" + mVisibleDialogTrackedIds + + " mInvisibleDialogTrackedIds=" + mInvisibleDialogTrackedIds); } - if (mVisibleTrackedIds == null) { + if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) { finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); } } + private void initialTrackedViews(AutofillId[] trackedIds, + @NonNull ArraySet<AutofillId> visibleSet, + @NonNull ArraySet<AutofillId> invisibleSet) { + final boolean[] isVisible; + final AutofillClient client = getClient(); + if (ArrayUtils.isEmpty(trackedIds) || client == null) { + return; + } + if (client.autofillClientIsVisibleForAutofill()) { + if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); + isVisible = client.autofillClientGetViewVisibility(trackedIds); + } else { + // All false + isVisible = new boolean[trackedIds.length]; + } + + final int numIds = trackedIds.length; + for (int i = 0; i < numIds; i++) { + final AutofillId id = trackedIds[i]; + id.resetSessionId(); + + if (isVisible[i]) { + addToSet(visibleSet, id); + } else { + addToSet(invisibleSet, id); + } + } + } + /** * Called when a {@link View view's} visibility changes. * @@ -3624,22 +3691,37 @@ public final class AutofillManager { if (isClientVisibleForAutofillLocked()) { if (isVisible) { if (isInSet(mInvisibleTrackedIds, id)) { - mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); - mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); + removeFromSet(mInvisibleTrackedIds, id); + addToSet(mVisibleTrackedIds, id); + } + if (isInSet(mInvisibleDialogTrackedIds, id)) { + removeFromSet(mInvisibleDialogTrackedIds, id); + addToSet(mVisibleDialogTrackedIds, id); } } else { if (isInSet(mVisibleTrackedIds, id)) { - mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); - mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); + removeFromSet(mVisibleTrackedIds, id); + addToSet(mInvisibleTrackedIds, id); + } + if (isInSet(mVisibleDialogTrackedIds, id)) { + removeFromSet(mVisibleDialogTrackedIds, id); + addToSet(mInvisibleDialogTrackedIds, id); } } } - if (mVisibleTrackedIds == null) { + if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) { if (sVerbose) { Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds); } finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); + + } + if (mVisibleDialogTrackedIds.isEmpty()) { + if (sVerbose) { + Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleDialogTrackedIds); + } + processNoVisibleTrackedAllViews(); } } @@ -3653,66 +3735,66 @@ public final class AutofillManager { // The visibility of the views might have changed while the client was not be visible, // hence update the visibility state for all views. AutofillClient client = getClient(); - ArraySet<AutofillId> updatedVisibleTrackedIds = null; - ArraySet<AutofillId> updatedInvisibleTrackedIds = null; if (client != null) { if (sVerbose) { Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds + " vis=" + mVisibleTrackedIds); } - if (mInvisibleTrackedIds != null) { - final ArrayList<AutofillId> orderedInvisibleIds = - new ArrayList<>(mInvisibleTrackedIds); - final boolean[] isVisible = client.autofillClientGetViewVisibility( - Helper.toArray(orderedInvisibleIds)); - - final int numInvisibleTrackedIds = orderedInvisibleIds.size(); - for (int i = 0; i < numInvisibleTrackedIds; i++) { - final AutofillId id = orderedInvisibleIds.get(i); - if (isVisible[i]) { - updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); - if (sDebug) { - Log.d(TAG, "onVisibleForAutofill() " + id + " became visible"); - } - } else { - updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); - } - } - } + onVisibleForAutofillChangedInternalLocked(mVisibleTrackedIds, mInvisibleTrackedIds); + onVisibleForAutofillChangedInternalLocked( + mVisibleDialogTrackedIds, mInvisibleDialogTrackedIds); + } - if (mVisibleTrackedIds != null) { - final ArrayList<AutofillId> orderedVisibleIds = - new ArrayList<>(mVisibleTrackedIds); - final boolean[] isVisible = client.autofillClientGetViewVisibility( - Helper.toArray(orderedVisibleIds)); + if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) { + if (sVerbose) { + Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); + } + finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); + } + if (mVisibleDialogTrackedIds.isEmpty()) { + if (sVerbose) { + Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); + } + processNoVisibleTrackedAllViews(); + } + } - final int numVisibleTrackedIds = orderedVisibleIds.size(); - for (int i = 0; i < numVisibleTrackedIds; i++) { - final AutofillId id = orderedVisibleIds.get(i); + void onVisibleForAutofillChangedInternalLocked(@NonNull ArraySet<AutofillId> visibleSet, + @NonNull ArraySet<AutofillId> invisibleSet) { + // The visibility of the views might have changed while the client was not be visible, + // hence update the visibility state for all views. + if (sVerbose) { + Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + invisibleSet + + " vis=" + visibleSet); + } - if (isVisible[i]) { - updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); - } else { - updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); + ArraySet<AutofillId> allTrackedIds = new ArraySet<>(); + allTrackedIds.addAll(visibleSet); + allTrackedIds.addAll(invisibleSet); + if (!allTrackedIds.isEmpty()) { + visibleSet.clear(); + invisibleSet.clear(); + initialTrackedViews(Helper.toArray(allTrackedIds), visibleSet, invisibleSet); + } + } - if (sDebug) { - Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible"); - } - } - } - } + private void processNoVisibleTrackedAllViews() { + mShowAutofillDialogCalled = false; + } - mInvisibleTrackedIds = updatedInvisibleTrackedIds; - mVisibleTrackedIds = updatedVisibleTrackedIds; + void checkViewState(AutofillId id) { + if (mAllTrackedViews.contains(id)) { + return; } - - if (mVisibleTrackedIds == null) { - if (sVerbose) { - Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); - } - finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); + // Add the id as tracked to avoid triggering fill request again and again. + mAllTrackedViews.add(id); + if (mHasNewTrackedView) { + return; } + // First one new tracks view + mIsFillRequested.set(false); + mHasNewTrackedView = true; } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 1235b602cde9..2dfe89397ea5 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1522,7 +1522,8 @@ public class LockPatternUtils { STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT}) + STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, + SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1575,11 +1576,18 @@ public class LockPatternUtils { public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80; /** + * Some authentication is required because the trustagent either timed out or was disabled + * manually. + */ + public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100; + + /** * Strong auth flags that do not prevent biometric methods from being accepted as auth. * If any other flags are set, biometric authentication is disabled. */ private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED - | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; + | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST + | SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray(); private final H mHandler; diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index c527e59e7c10..2d8f21d5c62c 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1974,7 +1974,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Замацаваць праграму \"<xliff:g id="LABEL">%1$s</xliff:g>\""</string> <string name="unpin_target" msgid="3963318576590204447">"Адмацаваць"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Адмацаваць праграму \"<xliff:g id="LABEL">%1$s</xliff:g>\""</string> - <string name="app_info" msgid="6113278084877079851">"Інфармацыя пра праграму"</string> + <string name="app_info" msgid="6113278084877079851">"Звесткі аб праграме"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Ідзе запуск дэманстрацыі…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Ідзе скід налад прылады…"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 0ec4f1d8e757..40b265af02db 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1167,8 +1167,8 @@ <string name="no" msgid="5122037903299899715">"Annuler"</string> <string name="dialog_alert_title" msgid="651856561974090712">"Attention"</string> <string name="loading" msgid="3138021523725055037">"Chargement en cours..."</string> - <string name="capital_on" msgid="2770685323900821829">"OUI"</string> - <string name="capital_off" msgid="7443704171014626777">"NON"</string> + <string name="capital_on" msgid="2770685323900821829">"ACTIVÉ"</string> + <string name="capital_off" msgid="7443704171014626777">"DÉSACTIVÉ"</string> <string name="checked" msgid="9179896827054513119">"coché"</string> <string name="not_checked" msgid="7972320087569023342">"non coché"</string> <string name="selected" msgid="6614607926197755875">"sélectionné"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 38a7c7b44474..b02345ac9945 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -626,15 +626,15 @@ </string-array> <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Terjadi error. Coba lagi."</string> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon sidik jari"</string> - <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string> - <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Masalah pada Face Unlock"</string> + <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Buka dengan Wajah"</string> + <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Masalah pada Buka dengan Wajah"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketuk untuk menghapus model wajah, lalu tambahkan wajah Anda lagi"</string> - <string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Face Unlock"</string> + <string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Buka dengan Wajah"</string> <string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci ponsel dengan melihat ke ponsel"</string> - <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Face Unlock, aktifkan "<b>"Akses kamera"</b>" di Setelan > Privasi"</string> + <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Buka dengan Wajah, aktifkan "<b>"Akses kamera"</b>" di Setelan > Privasi"</string> <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Siapkan lebih banyak cara untuk membuka kunci"</string> <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketuk untuk menambahkan sidik jari"</string> - <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string> + <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Buka dengan Sidik Jari"</string> <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Tidak dapat menggunakan sensor sidik jari"</string> <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Kunjungi penyedia reparasi."</string> <string name="face_acquired_insufficient" msgid="6889245852748492218">"Tidak dapat membuat model wajah Anda. Coba lagi."</string> @@ -667,19 +667,19 @@ <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Tidak dapat memverifikasi wajah. Hardware tidak tersedia."</string> - <string name="face_error_timeout" msgid="2598544068593889762">"Coba Face Unlock lagi"</string> + <string name="face_error_timeout" msgid="2598544068593889762">"Coba Buka dengan Wajah lagi"</string> <string name="face_error_no_space" msgid="5649264057026021723">"Tidak dapat menyimpan data wajah. Hapus dahulu data lama."</string> <string name="face_error_canceled" msgid="2164434737103802131">"Pemrosesan wajah dibatalkan."</string> - <string name="face_error_user_canceled" msgid="5766472033202928373">"Face Unlock dibatalkan oleh pengguna"</string> + <string name="face_error_user_canceled" msgid="5766472033202928373">"Buka dengan Wajah dibatalkan oleh pengguna"</string> <string name="face_error_lockout" msgid="7864408714994529437">"Terlalu banyak percobaan. Coba lagi nanti."</string> - <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Terlalu banyak upaya gagal. Face Unlock dinonaktifkan."</string> + <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Terlalu banyak upaya gagal. Buka dengan Wajah dinonaktifkan."</string> <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Terlalu banyak upaya gagal. Masukkan kunci layar."</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"Tidak dapat memverifikasi wajah. Coba lagi."</string> - <string name="face_error_not_enrolled" msgid="1134739108536328412">"Anda belum menyiapkan Face Unlock"</string> - <string name="face_error_hw_not_present" msgid="7940978724978763011">"Face Unlock tidak didukung di perangkat ini"</string> + <string name="face_error_not_enrolled" msgid="1134739108536328412">"Anda belum menyiapkan Buka dengan Wajah"</string> + <string name="face_error_hw_not_present" msgid="7940978724978763011">"Buka dengan Wajah tidak didukung di perangkat ini"</string> <string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor dinonaktifkan untuk sementara."</string> <string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> wajah"</string> - <string name="face_app_setting_name" msgid="5854024256907828015">"Gunakan Face Unlock"</string> + <string name="face_app_setting_name" msgid="5854024256907828015">"Gunakan Buka dengan Wajah"</string> <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gunakan face lock atau kunci layar"</string> <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Gunakan wajah untuk melanjutkan"</string> <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Gunakan face lock atau kunci layar untuk melanjutkan"</string> @@ -928,7 +928,7 @@ <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Coba lagi"</string> <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Coba lagi"</string> <string name="lockscreen_storage_locked" msgid="634993789186443380">"Membuka kunci untuk semua fitur dan data"</string> - <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Percobaan Face Unlock melebihi batas maksimum"</string> + <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Percobaan Buka dengan Wajah melebihi batas maksimum"</string> <string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Tidak ada kartu SIM"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Tidak ada kartu SIM dalam tablet."</string> <string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Tidak ada kartu SIM di perangkat Android TV."</string> @@ -998,7 +998,7 @@ <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Luaskan area buka kunci."</string> <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Buka kunci dengan menggeser."</string> <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Buka kunci dengan pola."</string> - <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Face Unlock."</string> + <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Buka dengan Wajah."</string> <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Buka kunci dengan PIN."</string> <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"PIN SIM terbuka."</string> <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"PUK SIM terbuka."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 6b88f8ef39c2..42a477b4b1a8 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -897,7 +897,7 @@ <string name="relationTypeFriend" msgid="3192092625893980574">"Amigo(a)"</string> <string name="relationTypeManager" msgid="2272860813153171857">"Gerente"</string> <string name="relationTypeMother" msgid="2331762740982699460">"Mãe"</string> - <string name="relationTypeParent" msgid="4177920938333039882">"Pai/Mãe"</string> + <string name="relationTypeParent" msgid="4177920938333039882">"familiar responsável"</string> <string name="relationTypePartner" msgid="4018017075116766194">"Parceiro"</string> <string name="relationTypeReferredBy" msgid="5285082289602849400">"Indicado por"</string> <string name="relationTypeRelative" msgid="3396498519818009134">"Parente"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 6b88f8ef39c2..42a477b4b1a8 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -897,7 +897,7 @@ <string name="relationTypeFriend" msgid="3192092625893980574">"Amigo(a)"</string> <string name="relationTypeManager" msgid="2272860813153171857">"Gerente"</string> <string name="relationTypeMother" msgid="2331762740982699460">"Mãe"</string> - <string name="relationTypeParent" msgid="4177920938333039882">"Pai/Mãe"</string> + <string name="relationTypeParent" msgid="4177920938333039882">"familiar responsável"</string> <string name="relationTypePartner" msgid="4018017075116766194">"Parceiro"</string> <string name="relationTypeReferredBy" msgid="5285082289602849400">"Indicado por"</string> <string name="relationTypeRelative" msgid="3396498519818009134">"Parente"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index bfb7a38fa269..01e57e130420 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -993,7 +993,7 @@ <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Výber používateľa"</string> <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Stav"</string> <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Fotoaparát"</string> - <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Ovládacie prvky médií"</string> + <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Ovládanie médií"</string> <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Zmena usporiadania miniaplikácií sa začala."</string> <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Zmena usporiadania miniaplikácií sa skončila."</string> <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Miniaplikácia <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> bola odstránená."</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 8647c5c7b538..f0470220e2fa 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -364,7 +364,7 @@ <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. మీ డివైజ్కు వచ్చిన మెసేజ్లను మీకు చూపకుండానే యాప్ పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string> <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార మెసేజ్లను ఫార్వర్డ్ చేయడం"</string> <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార మెసేజ్లను స్వీకరించినప్పుడు, వాటిని ఫార్వర్డ్ చేయడానికి సెల్ ప్రసార మాడ్యూల్కు కట్టుబడి ఉండేందుకు యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string> - <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్ను మేనేజ్ చేయి"</string> + <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్ను మేనేజ్ చేయండి"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"మీ పరికరంలో కొనసాగుతున్న కాల్స్ను చూడటానికి అలాగే వాటిని కంట్రోల్ చేయడానికి ఒక యాప్కు అనుమతిస్తోంది."</string> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార మెసేజ్లను చదవడం"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string> @@ -1149,7 +1149,7 @@ <string name="redo" msgid="7231448494008532233">"చర్యను రిపీట్ చేయి"</string> <string name="autofill" msgid="511224882647795296">"ఆటోఫిల్"</string> <string name="textSelectionCABTitle" msgid="5151441579532476940">"వచన ఎంపిక"</string> - <string name="addToDictionary" msgid="8041821113480950096">"నిఘంటువుకు జోడించు"</string> + <string name="addToDictionary" msgid="8041821113480950096">"నిఘంటువుకు జోడించండి"</string> <string name="deleteText" msgid="4200807474529938112">"తొలగించండి"</string> <string name="inputMethod" msgid="1784759500516314751">"ఇన్పుట్ పద్ధతి"</string> <string name="editTextMenuTitle" msgid="857666911134482176">"వచనానికి సంబంధించిన చర్యలు"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bbbb79c79527..22f42980d858 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4789,11 +4789,11 @@ <integer name="config_defaultPeakRefreshRate">0</integer> <!-- The display uses different gamma curves for different refresh rates. It's hard for panel - vendor to tune the curves to have exact same brightness for different refresh rate. So + vendors to tune the curves to have exact same brightness for different refresh rate. So flicker could be observed at switch time. The issue is worse at the gamma lower end. In addition, human eyes are more sensitive to the flicker at darker environment. To prevent flicker, we only support higher refresh rates if the display brightness is above - a threshold. And the darker environment could have higher threshold. + a threshold. For example, no higher refresh rate if display brightness <= disp0 && ambient brightness <= amb0 || display brightness <= disp1 && ambient brightness <= amb1 --> @@ -4815,13 +4815,12 @@ <integer name="config_defaultRefreshRateInZone">0</integer> <!-- The display uses different gamma curves for different refresh rates. It's hard for panel - vendor to tune the curves to have exact same brightness for different refresh rate. So + vendors to tune the curves to have exact same brightness for different refresh rate. So flicker could be observed at switch time. The issue can be observed on the screen with even full white content at the high brightness. To prevent flickering, we support fixed refresh rates if the display and ambient brightness are equal to or above the provided thresholds. You can define multiple threshold levels as higher brightness environments - may have lower display brightness requirements for the flickering is visible. And the - high brightness environment could have higher threshold. + may have lower display brightness requirements for the flickering is visible. For example, fixed refresh rate if display brightness >= disp0 && ambient brightness >= amb0 || display brightness >= disp1 && ambient brightness >= amb1 --> diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java index 4679a9ea6f66..0b7019995acb 100644 --- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java @@ -19,6 +19,9 @@ package com.android.internal.util; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; @@ -37,6 +40,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.UserInfo; +import android.os.Looper; import android.os.RemoteException; import android.os.UserManager; import android.provider.Settings; @@ -233,6 +237,45 @@ public class LockPatternUtilsTest { ComponentName.unflattenFromString("com.test/.TestAgent")); } + @Test + public void isBiometricAllowedForUser_afterTrustagentExpired_returnsTrue() + throws RemoteException { + TestStrongAuthTracker tracker = createStrongAuthTracker(); + tracker.changeStrongAuth(SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED); + + assertTrue(tracker.isBiometricAllowedForUser( + /* isStrongBiometric = */ true, + DEMO_USER_ID)); + } + + @Test + public void isBiometricAllowedForUser_afterLockout_returnsFalse() + throws RemoteException { + TestStrongAuthTracker tracker = createStrongAuthTracker(); + tracker.changeStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT); + + assertFalse(tracker.isBiometricAllowedForUser( + /* isStrongBiometric = */ true, + DEMO_USER_ID)); + } + + + private TestStrongAuthTracker createStrongAuthTracker() { + final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext()); + return new TestStrongAuthTracker(context, Looper.getMainLooper()); + } + + private static class TestStrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + + TestStrongAuthTracker(Context context, Looper looper) { + super(context, looper); + } + + public void changeStrongAuth(@StrongAuthFlags int strongAuthFlags) { + handleStrongAuthRequiredChanged(strongAuthFlags, DEMO_USER_ID); + } + } + private ILockSettings createTestLockSettings() { final Context context = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); mLockPatternUtils = spy(new LockPatternUtils(context)); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a4a38c713a66..86c8097ee674 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3871,12 +3871,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, - "1648338379": { - "message": "Display id=%d is ignoring all orientation requests, return %d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "1653025361": { "message": "Register task fragment organizer=%s uid=%d pid=%d", "level": "VERBOSE", @@ -4117,6 +4111,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java" }, + "1877863087": { + "message": "Display id=%d is ignoring orientation request for %d, return %d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "1878927091": { "message": "prepareSurface: No changes in animation for %s", "level": "VERBOSE", diff --git a/data/keyboards/Vendor_18d1_Product_4f80.kl b/data/keyboards/Vendor_18d1_Product_4f80.kl new file mode 100644 index 000000000000..42a692508bdd --- /dev/null +++ b/data/keyboards/Vendor_18d1_Product_4f80.kl @@ -0,0 +1,21 @@ +# 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. + +# +# Android Stylus +# + +# Temporarily map the key reported by the stylus to an +# arbitrary keycode for testing and development. +key 0x242 VIDEO_APP_1 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 d3dc05fb92d4..405ee14c2346 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -425,12 +425,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (container != null) { // Cleanup if the TaskFragment vanished is not requested by the organizer. removeContainer(container); - // Make sure the top container is updated. - final TaskFragmentContainer newTopContainer = getTopActiveContainer( - container.getTaskId()); - if (newTopContainer != null) { - updateContainer(wct, newTopContainer); - } + // Make sure the containers in the Task are up-to-date. + updateContainersInTaskIfVisible(wct, container.getTaskId()); } cleanupTaskFragment(taskFragmentInfo.getFragmentToken()); } @@ -470,6 +466,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen updateContainersInTask(wct, taskContainer); } + void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) { + final TaskContainer taskContainer = getTaskContainer(taskId); + if (taskContainer != null && taskContainer.isVisible()) { + updateContainersInTask(wct, taskContainer); + } + } + private void updateContainersInTask(@NonNull WindowContainerTransaction wct, @NonNull TaskContainer taskContainer) { // Update all TaskFragments in the Task. Make a copy of the list since some may be @@ -1320,9 +1323,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void removeContainer(@NonNull TaskFragmentContainer container) { // Remove all split containers that included this one final TaskContainer taskContainer = container.getTaskContainer(); - if (taskContainer == null) { - return; - } taskContainer.mContainers.remove(container); // Marked as a pending removal which will be removed after it is actually removed on the // server side (#onTaskFragmentVanished). @@ -1515,14 +1515,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final TaskFragmentContainer container = getContainerWithActivity(activity); - // Don't launch placeholder if the container is occluded. - if (container != null && container != getTopActiveContainer(container.getTaskId())) { - return false; - } - - final SplitContainer splitContainer = getActiveSplitForContainer(container); - if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { - // Don't launch placeholder in primary split container + if (container != null && !allowLaunchPlaceholder(container)) { + // We don't allow activity in this TaskFragment to launch placeholder. return false; } @@ -1550,6 +1544,32 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } + /** Whether or not to allow activity in this container to launch placeholder. */ + @GuardedBy("mLock") + private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) { + final TaskFragmentContainer topContainer = getTopActiveContainer(container.getTaskId()); + if (container != topContainer) { + // The container is not the top most. + if (!container.isVisible()) { + // In case the container is visible (the one on top may be transparent), we may + // still want to launch placeholder even if it is not the top most. + return false; + } + if (topContainer.isWaitingActivityAppear()) { + // When the top container appeared info is not sent by the server yet, the visible + // check above may not be reliable. + return false; + } + } + + final SplitContainer splitContainer = getActiveSplitForContainer(container); + if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { + // Don't launch placeholder for primary split container. + return false; + } + return true; + } + /** * Gets the activity options for starting the placeholder activity. In case the placeholder is * launched when the Task is in the background, we don't want to bring the Task to the front. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 47253d388f0d..a432e2b10c14 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -154,12 +154,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { void cleanupContainer(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container, boolean shouldFinishDependent) { container.finish(shouldFinishDependent, this, wct, mController); - - final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer( - container.getTaskId()); - if (newTopContainer != null) { - mController.updateContainer(wct, newTopContainer); - } + // Make sure the containers in the Task is up-to-date. + mController.updateContainersInTaskIfVisible(wct, container.getTaskId()); } /** 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 fcf0ac78af38..8240874ca53d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -184,6 +184,11 @@ class TaskFragmentContainer { return allActivities; } + /** Whether this TaskFragment is visible. */ + boolean isVisible() { + return mInfo != null && mInfo.isVisible(); + } + /** Whether the TaskFragment is in an intermediate state waiting for the server update.*/ boolean isInIntermediateState() { if (mInfo == 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 b70b320eee3c..9b16877e39e7 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -35,7 +35,6 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.util.ArrayMap; -import android.window.WindowProvider; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -129,9 +128,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { }); mWindowLayoutChangeListeners.put(context, consumer); - // TODO(b/258065175) Further extend this to ContextWrappers. - if (context instanceof WindowProvider) { - final IBinder windowContextToken = context.getWindowContextToken(); + final IBinder windowContextToken = context.getWindowContextToken(); + if (windowContextToken != null) { + // We register component callbacks for window contexts. For activity contexts, they will + // receive callbacks from NotifyOnConfigurationChanged instead. final ConfigurationChangeListener listener = new ConfigurationChangeListener(windowContextToken); context.registerComponentCallbacks(listener); @@ -150,8 +150,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { if (!mWindowLayoutChangeListeners.get(context).equals(consumer)) { continue; } - if (context instanceof WindowProvider) { - final IBinder token = context.getWindowContextToken(); + final IBinder token = context.getWindowContextToken(); + if (token != null) { context.unregisterComponentCallbacks(mConfigurationChangeListeners.get(token)); mConfigurationChangeListeners.remove(token); } @@ -308,9 +308,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { return false; } final int windowingMode; - if (context instanceof Activity) { + IBinder activityToken = context.getActivityToken(); + if (activityToken != null) { final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration( - context.getActivityToken()); + activityToken); if (taskConfig == null) { // If we cannot determine the task configuration for any reason, it is likely that // we won't be able to determine its position correctly as well. DisplayFeatures' diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java index 7624b693ac43..fe60037483c4 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java @@ -27,9 +27,10 @@ import java.util.function.Consumer; */ public class AcceptOnceConsumer<T> implements Consumer<T> { private final Consumer<T> mCallback; - private final DataProducer<T> mProducer; + private final AcceptOnceProducerCallback<T> mProducer; - public AcceptOnceConsumer(@NonNull DataProducer<T> producer, @NonNull Consumer<T> callback) { + public AcceptOnceConsumer(@NonNull AcceptOnceProducerCallback<T> producer, + @NonNull Consumer<T> callback) { mProducer = producer; mCallback = callback; } @@ -37,6 +38,20 @@ public class AcceptOnceConsumer<T> implements Consumer<T> { @Override public void accept(@NonNull T t) { mCallback.accept(t); - mProducer.removeDataChangedCallback(this); + mProducer.onConsumerReadyToBeRemoved(this); + } + + /** + * Interface to allow the {@link AcceptOnceConsumer} to notify the client that created it, + * when it is ready to be removed. This allows the client to remove the consumer object + * when it deems it is safe to do so. + * @param <T> The type of data this callback accepts through {@link #onConsumerReadyToBeRemoved} + */ + public interface AcceptOnceProducerCallback<T> { + + /** + * Notifies that the given {@code callback} is ready to be removed + */ + void onConsumerReadyToBeRemoved(Consumer<T> callback); } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java index cbaa27712015..46c925aaf8a2 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java @@ -19,6 +19,7 @@ package androidx.window.util; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Optional; import java.util.Set; @@ -31,11 +32,14 @@ import java.util.function.Consumer; * * @param <T> The type of data this producer returns through {@link DataProducer#getData}. */ -public abstract class BaseDataProducer<T> implements DataProducer<T> { +public abstract class BaseDataProducer<T> implements DataProducer<T>, + AcceptOnceConsumer.AcceptOnceProducerCallback<T> { private final Object mLock = new Object(); @GuardedBy("mLock") private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>(); + @GuardedBy("mLock") + private final Set<Consumer<T>> mCallbacksToRemove = new HashSet<>(); /** * Adds a callback to the set of callbacks listening for data. Data is delivered through @@ -85,6 +89,26 @@ public abstract class BaseDataProducer<T> implements DataProducer<T> { for (Consumer<T> callback : mCallbacks) { callback.accept(value); } + removeFinishedCallbacksLocked(); + } + } + + /** + * Removes any callbacks that notified us through {@link #onConsumerReadyToBeRemoved(Consumer)} + * that they are ready to be removed. + */ + @GuardedBy("mLock") + private void removeFinishedCallbacksLocked() { + for (Consumer<T> callback: mCallbacksToRemove) { + mCallbacks.remove(callback); + } + mCallbacksToRemove.clear(); + } + + @Override + public void onConsumerReadyToBeRemoved(Consumer<T> callback) { + synchronized (mLock) { + mCallbacksToRemove.add(callback); } } }
\ No newline at end of file diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java index bc03e4ec303d..2f92a577baa2 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java @@ -163,11 +163,17 @@ public class EmbeddingTestUtils { /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, @NonNull Activity activity) { + return createMockTaskFragmentInfo(container, activity, true /* isVisible */); + } + + /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ + static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, + @NonNull Activity activity, boolean isVisible) { return new TaskFragmentInfo(container.getTaskFragmentToken(), mock(WindowContainerToken.class), new Configuration(), 1, - true /* isVisible */, + isVisible, Collections.singletonList(activity.getActivityToken()), new Point(), false /* isTaskClearedForReuse */, 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 6725dfdb94e8..3cc31f9761c1 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 @@ -1254,6 +1254,68 @@ public class SplitControllerTest { verify(mEmbeddingCallback).accept(any()); } + @Test + public void testLaunchPlaceholderIfNecessary_nonEmbeddedActivity() { + // Launch placeholder for non embedded activity. + setupPlaceholderRule(mActivity); + mTransactionManager.startNewTransaction(); + mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, + true /* isOnCreated */); + + verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT), + any()); + } + + @Test + public void testLaunchPlaceholderIfNecessary_embeddedInTopTaskFragment() { + // Launch placeholder for activity in top TaskFragment. + setupPlaceholderRule(mActivity); + mTransactionManager.startNewTransaction(); + final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID); + mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, + true /* isOnCreated */); + + assertTrue(container.hasActivity(mActivity.getActivityToken())); + verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT), + any()); + } + + @Test + public void testLaunchPlaceholderIfNecessary_embeddedBelowTaskFragment() { + // Do not launch placeholder for invisible activity below the top TaskFragment. + setupPlaceholderRule(mActivity); + mTransactionManager.startNewTransaction(); + final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID); + final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity, + TASK_ID); + bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity, + false /* isVisible */)); + topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); + assertFalse(bottomTf.isVisible()); + mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, + true /* isOnCreated */); + + verify(mTransaction, never()).startActivityInTaskFragment(any(), any(), any(), any()); + } + + @Test + public void testLaunchPlaceholderIfNecessary_embeddedBelowTransparentTaskFragment() { + // Launch placeholder for visible activity below the top TaskFragment. + setupPlaceholderRule(mActivity); + mTransactionManager.startNewTransaction(); + final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID); + final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity, + TASK_ID); + bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity, + true /* isVisible */)); + topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); + assertTrue(bottomTf.isVisible()); + mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, + true /* isOnCreated */); + + verify(mTransaction).startActivityInTaskFragment(any(), any(), any(), any()); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); 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 5c3ba72e2361..98772360a321 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 @@ -500,6 +500,29 @@ public class TaskFragmentContainerTest { assertEquals(2, taskContainer.indexOf(tf1)); } + @Test + public void testIsVisible() { + final TaskContainer taskContainer = createTestTaskContainer(); + final TaskFragmentContainer container = new TaskFragmentContainer( + null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, + null /* pairedPrimaryTaskFragment */); + + // Not visible when there is not appeared. + assertFalse(container.isVisible()); + + // Respect info.isVisible. + TaskFragmentInfo info = createMockTaskFragmentInfo(container, mActivity, + true /* isVisible */); + container.setInfo(mTransaction, info); + + assertTrue(container.isVisible()); + + info = createMockTaskFragmentInfo(container, mActivity, false /* isVisible */); + container.setInfo(mTransaction, info); + + assertFalse(container.isVisible()); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { final Activity activity = mock(Activity.class); diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 4a937cf92d12..4b10f5a1a886 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -33,8 +33,7 @@ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string> <string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string> - <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) --> - <skip /> + <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"यो एप एउटा विन्डोमा मात्र खोल्न मिल्छ।"</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string> <string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string> 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 c743582c3264..09f5cf1d31e4 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 @@ -61,6 +61,7 @@ import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; import com.android.wm.shell.draganddrop.DragAndDropController; @@ -677,7 +678,11 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional<DesktopMode> provideDesktopMode( - Optional<DesktopModeController> desktopModeController) { + Optional<DesktopModeController> desktopModeController, + Optional<DesktopTasksController> desktopTasksController) { + if (DesktopModeStatus.isProto2Enabled()) { + return desktopTasksController.map(DesktopTasksController::asDesktopMode); + } return desktopModeController.map(DesktopModeController::asDesktopMode); } @@ -700,6 +705,23 @@ public abstract class WMShellBaseModule { @BindsOptionalOf @DynamicOverride + abstract DesktopTasksController optionalDesktopTasksController(); + + @WMSingleton + @Provides + static Optional<DesktopTasksController> providesDesktopTasksController( + @DynamicOverride Optional<Lazy<DesktopTasksController>> desktopTasksController) { + // Use optional-of-lazy for the dependency that this provider relies on. + // Lazy ensures that this provider will not be the cause the dependency is created + // when it will not be returned due to the condition below. + if (DesktopModeStatus.isProto2Enabled()) { + return desktopTasksController.map(Lazy::get); + } + return Optional.empty(); + } + + @BindsOptionalOf + @DynamicOverride abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository(); @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 6be83054ae59..701a3a42fadd 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 @@ -50,6 +50,7 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; @@ -189,7 +190,8 @@ public abstract class WMShellModule { ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue, - Optional<DesktopModeController> desktopModeController) { + Optional<DesktopModeController> desktopModeController, + Optional<DesktopTasksController> desktopTasksController) { return new CaptionWindowDecorViewModel( context, mainHandler, @@ -197,7 +199,8 @@ public abstract class WMShellModule { taskOrganizer, displayController, syncQueue, - desktopModeController); + desktopModeController, + desktopTasksController); } // @@ -616,6 +619,22 @@ public abstract class WMShellModule { @WMSingleton @Provides @DynamicOverride + static DesktopTasksController provideDesktopTasksController( + Context context, + ShellInit shellInit, + ShellController shellController, + ShellTaskOrganizer shellTaskOrganizer, + Transitions transitions, + @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, + @ShellMainThread ShellExecutor mainExecutor + ) { + return new DesktopTasksController(context, shellInit, shellController, shellTaskOrganizer, + transitions, desktopModeTaskRepository, mainExecutor); + } + + @WMSingleton + @Provides + @DynamicOverride static DesktopModeTaskRepository provideDesktopModeTaskRepository() { return new DesktopModeTaskRepository(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index 67f4a1914c49..055949fd8c89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -70,9 +70,13 @@ public class DesktopModeStatus { * @return {@code true} if active */ public static boolean isActive(Context context) { - if (!IS_SUPPORTED) { + if (!isAnyEnabled()) { return false; } + if (isProto2Enabled()) { + // Desktop mode is always active in prototype 2 + return true; + } try { int result = Settings.System.getIntForUser(context.getContentResolver(), Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt new file mode 100644 index 000000000000..b075b14fb0a4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -0,0 +1,231 @@ +/* + * 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.desktopmode + +import android.app.ActivityManager +import android.app.WindowConfiguration +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.app.WindowConfiguration.WindowingMode +import android.content.Context +import android.view.WindowManager +import android.window.WindowContainerTransaction +import androidx.annotation.BinderThread +import com.android.internal.protolog.common.ProtoLog +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.ExecutorUtils +import com.android.wm.shell.common.ExternalInterfaceBinder +import com.android.wm.shell.common.RemoteCallable +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.annotations.ExternalThread +import com.android.wm.shell.common.annotations.ShellMainThread +import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.sysui.ShellSharedConstants +import com.android.wm.shell.transition.Transitions +import java.util.concurrent.Executor +import java.util.function.Consumer + +/** Handles moving tasks in and out of desktop */ +class DesktopTasksController( + private val context: Context, + shellInit: ShellInit, + private val shellController: ShellController, + private val shellTaskOrganizer: ShellTaskOrganizer, + private val transitions: Transitions, + private val desktopModeTaskRepository: DesktopModeTaskRepository, + @ShellMainThread private val mainExecutor: ShellExecutor +) : RemoteCallable<DesktopTasksController> { + + private val desktopMode: DesktopModeImpl + + init { + desktopMode = DesktopModeImpl() + if (DesktopModeStatus.isProto2Enabled()) { + shellInit.addInitCallback({ onInit() }, this) + } + } + + private fun onInit() { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") + shellController.addExternalInterface( + ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE, + { createExternalInterface() }, + this + ) + } + + /** Show all tasks, that are part of the desktop, on top of launcher */ + fun showDesktopApps() { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps") + val wct = WindowContainerTransaction() + + bringDesktopAppsToFront(wct) + + // Execute transaction if there are pending operations + if (!wct.isEmpty) { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + } + + /** Move a task with given `taskId` to desktop */ + fun moveToDesktop(taskId: Int) { + shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task) } + } + + /** Move a task to desktop */ + fun moveToDesktop(task: ActivityManager.RunningTaskInfo) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDesktop: %d", task.taskId) + + val wct = WindowContainerTransaction() + // Bring other apps to front first + bringDesktopAppsToFront(wct) + + wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FREEFORM) + wct.reorder(task.getToken(), true /* onTop */) + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + + /** Move a task with given `taskId` to fullscreen */ + fun moveToFullscreen(taskId: Int) { + shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToFullscreen(task) } + } + + /** Move a task to fullscreen */ + fun moveToFullscreen(task: ActivityManager.RunningTaskInfo) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId) + + val wct = WindowContainerTransaction() + wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FULLSCREEN) + wct.setBounds(task.getToken(), null) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + + /** + * Get windowing move for a given `taskId` + * + * @return [WindowingMode] for the task or [WINDOWING_MODE_UNDEFINED] if task is not found + */ + @WindowingMode + fun getTaskWindowingMode(taskId: Int): Int { + return shellTaskOrganizer.getRunningTaskInfo(taskId)?.windowingMode + ?: WINDOWING_MODE_UNDEFINED + } + + private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) { + val activeTasks = desktopModeTaskRepository.getActiveTasks() + + // Skip if all tasks are already visible + if (activeTasks.isNotEmpty() && activeTasks.all(desktopModeTaskRepository::isVisibleTask)) { + ProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "bringDesktopAppsToFront: active tasks are already in front, skipping." + ) + return + } + + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront") + + // First move home to front and then other tasks on top of it + moveHomeTaskToFront(wct) + + val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder() + activeTasks + // Sort descending as the top task is at index 0. It should be ordered to top last + .sortedByDescending { taskId -> allTasksInZOrder.indexOf(taskId) } + .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) } + .forEach { task -> wct.reorder(task.token, true /* onTop */) } + } + + private fun moveHomeTaskToFront(wct: WindowContainerTransaction) { + shellTaskOrganizer + .getRunningTasks(context.displayId) + .firstOrNull { task -> task.activityType == ACTIVITY_TYPE_HOME } + ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } + } + + override fun getContext(): Context { + return context + } + + override fun getRemoteCallExecutor(): ShellExecutor { + return mainExecutor + } + + /** Creates a new instance of the external interface to pass to another process. */ + private fun createExternalInterface(): ExternalInterfaceBinder { + return IDesktopModeImpl(this) + } + + /** Get connection interface between sysui and shell */ + fun asDesktopMode(): DesktopMode { + return desktopMode + } + + /** + * Adds a listener to find out about changes in the visibility of freeform tasks. + * + * @param listener the listener to add. + * @param callbackExecutor the executor to call the listener on. + */ + fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) { + desktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor) + } + + /** The interface for calls from outside the shell, within the host process. */ + @ExternalThread + private inner class DesktopModeImpl : DesktopMode { + override fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) { + mainExecutor.execute { + this@DesktopTasksController.addListener(listener, callbackExecutor) + } + } + } + + /** The interface for calls from outside the host process. */ + @BinderThread + private class IDesktopModeImpl(private var controller: DesktopTasksController?) : + IDesktopMode.Stub(), ExternalInterfaceBinder { + /** Invalidates this instance, preventing future calls from updating the controller. */ + override fun invalidate() { + controller = null + } + + override fun showDesktopApps() { + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "showDesktopApps", + Consumer(DesktopTasksController::showDesktopApps) + ) + } + } +} 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 5be29db12b25..717ae91f74c5 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 @@ -1613,7 +1613,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.flingDividerToDismiss( mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT, EXIT_REASON_APP_FINISHED); - } else if (!isSplitScreenVisible()) { + } else if (!isSplitScreenVisible() && !mIsSplitEntering) { exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED); } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 42e2b3fadf19..299284f5bda6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.transition.Transitions; @@ -75,6 +76,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final SyncTransactionQueue mSyncQueue; private FreeformTaskTransitionStarter mTransitionStarter; private Optional<DesktopModeController> mDesktopModeController; + private Optional<DesktopTasksController> mDesktopTasksController; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); @@ -90,7 +92,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue, - Optional<DesktopModeController> desktopModeController) { + Optional<DesktopModeController> desktopModeController, + Optional<DesktopTasksController> desktopTasksController) { this( context, mainHandler, @@ -99,6 +102,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { displayController, syncQueue, desktopModeController, + desktopTasksController, new CaptionWindowDecoration.Factory(), new InputMonitorFactory()); } @@ -112,6 +116,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, + Optional<DesktopTasksController> desktopTasksController, CaptionWindowDecoration.Factory captionWindowDecorFactory, InputMonitorFactory inputMonitorFactory) { mContext = context; @@ -122,6 +127,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDisplayController = displayController; mSyncQueue = syncQueue; mDesktopModeController = desktopModeController; + mDesktopTasksController = desktopTasksController; mCaptionWindowDecorFactory = captionWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; @@ -242,11 +248,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { decoration.createHandleMenu(); } else if (id == R.id.desktop_button) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); + mDesktopTasksController.ifPresent(c -> c.moveToDesktop(mTaskId)); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); + mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId)); decoration.closeHandleMenu(); - decoration.setButtonVisibility(); + decoration.setButtonVisibility(false); } } @@ -299,8 +307,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { */ private void handleEventForMove(MotionEvent e) { RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); - if (mDesktopModeController.isPresent() - && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId) + if (DesktopModeStatus.isProto2Enabled() + && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + return; + } + if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent() + && mDesktopModeController.get().getDisplayAreaWindowingMode( + taskInfo.displayId) == WINDOWING_MODE_FULLSCREEN) { return; } @@ -324,9 +337,20 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { .stableInsets().top; mDragResizeCallback.onDragResizeEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); - if (e.getRawY(dragPointerIdx) <= statusBarHeight - && DesktopModeStatus.isActive(mContext)) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); + if (e.getRawY(dragPointerIdx) <= statusBarHeight) { + if (DesktopModeStatus.isProto2Enabled()) { + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + // Switch a single task to fullscreen + mDesktopTasksController.ifPresent( + c -> c.moveToFullscreen(taskInfo)); + } + } else if (DesktopModeStatus.isProto1Enabled()) { + if (DesktopModeStatus.isActive(mContext)) { + // Turn off desktop mode + mDesktopModeController.ifPresent( + c -> c.setDesktopModeActive(false)); + } + } } break; } @@ -408,13 +432,27 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { * @param ev the {@link MotionEvent} received by {@link EventReceiver} */ private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) { - if (!DesktopModeStatus.isActive(mContext)) { - handleCaptionThroughStatusBar(ev); + if (DesktopModeStatus.isProto2Enabled()) { + CaptionWindowDecoration focusedDecor = getFocusedDecor(); + if (focusedDecor == null + || focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + handleCaptionThroughStatusBar(ev); + } + } else if (DesktopModeStatus.isProto1Enabled()) { + if (!DesktopModeStatus.isActive(mContext)) { + handleCaptionThroughStatusBar(ev); + } } handleEventOutsideFocusedCaption(ev); // Prevent status bar from reacting to a caption drag. - if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) { - inputMonitor.pilferPointers(); + if (DesktopModeStatus.isProto2Enabled()) { + if (mTransitionDragActive) { + inputMonitor.pilferPointers(); + } + } else if (DesktopModeStatus.isProto1Enabled()) { + if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) { + inputMonitor.pilferPointers(); + } } } @@ -443,9 +481,20 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { case MotionEvent.ACTION_DOWN: { // Begin drag through status bar if applicable. CaptionWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor != null && !DesktopModeStatus.isActive(mContext) - && focusedDecor.checkTouchEventInHandle(ev)) { - mTransitionDragActive = true; + if (focusedDecor != null) { + boolean dragFromStatusBarAllowed = false; + if (DesktopModeStatus.isProto2Enabled()) { + // In proto2 any full screen task can be dragged to freeform + dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode() + == WINDOWING_MODE_FULLSCREEN; + } else if (DesktopModeStatus.isProto1Enabled()) { + // In proto1 task can be dragged to freeform when not in desktop mode + dragFromStatusBarAllowed = !DesktopModeStatus.isActive(mContext); + } + + if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) { + mTransitionDragActive = true; + } } break; } @@ -460,7 +509,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { int statusBarHeight = mDisplayController .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top; if (ev.getY() > statusBarHeight) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); + if (DesktopModeStatus.isProto2Enabled()) { + mDesktopTasksController.ifPresent( + c -> c.moveToDesktop(focusedDecor.mTaskInfo)); + } else if (DesktopModeStatus.isProto1Enabled()) { + mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); + } + return; } } 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 037ca2031254..f7c7a87e6659 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 @@ -16,8 +16,9 @@ package com.android.wm.shell.windowdecor; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; + import android.app.ActivityManager; -import android.app.WindowConfiguration; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -117,7 +118,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL ? R.dimen.freeform_decor_shadow_focused_thickness : R.dimen.freeform_decor_shadow_unfocused_thickness; final boolean isFreeform = - taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM; + taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; final boolean isDragResizeable = isFreeform && taskInfo.isResizeable; WindowDecorLinearLayout oldRootView = mResult.mRootView; @@ -167,11 +168,17 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL // If this task is not focused, do not show caption. setCaptionVisibility(mTaskInfo.isFocused); - // Only handle should show if Desktop Mode is inactive. - boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext); - if (mDesktopActive != desktopCurrentStatus && mTaskInfo.isFocused) { - mDesktopActive = desktopCurrentStatus; - setButtonVisibility(); + if (mTaskInfo.isFocused) { + if (DesktopModeStatus.isProto2Enabled()) { + updateButtonVisibility(); + } else if (DesktopModeStatus.isProto1Enabled()) { + // Only handle should show if Desktop Mode is inactive. + boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext); + if (mDesktopActive != desktopCurrentStatus) { + mDesktopActive = desktopCurrentStatus; + setButtonVisibility(mDesktopActive); + } + } } if (!isDragResizeable) { @@ -214,7 +221,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL View handle = caption.findViewById(R.id.caption_handle); handle.setOnTouchListener(mOnCaptionTouchListener); handle.setOnClickListener(mOnCaptionButtonClickListener); - setButtonVisibility(); + updateButtonVisibility(); } private void setupHandleMenu() { @@ -244,14 +251,25 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL /** * Sets the visibility of buttons and color of caption based on desktop mode status */ - void setButtonVisibility() { - mDesktopActive = DesktopModeStatus.isActive(mContext); - int v = mDesktopActive ? View.VISIBLE : View.GONE; + void updateButtonVisibility() { + if (DesktopModeStatus.isProto2Enabled()) { + setButtonVisibility(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM); + } else if (DesktopModeStatus.isProto1Enabled()) { + mDesktopActive = DesktopModeStatus.isActive(mContext); + setButtonVisibility(mDesktopActive); + } + } + + /** + * Show or hide buttons + */ + void setButtonVisibility(boolean visible) { + int visibility = visible ? View.VISIBLE : View.GONE; View caption = mResult.mRootView.findViewById(R.id.caption); View back = caption.findViewById(R.id.back_button); View close = caption.findViewById(R.id.close_window); - back.setVisibility(v); - close.setVisibility(v); + back.setVisibility(visibility); + close.setVisibility(visibility); int buttonTintColorRes = mDesktopActive ? R.color.decor_button_dark_color : R.color.decor_button_light_color; @@ -260,7 +278,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL View handle = caption.findViewById(R.id.caption_handle); VectorDrawable handleBackground = (VectorDrawable) handle.getBackground(); handleBackground.setTintList(buttonTintColor); - caption.getBackground().setTint(v == View.VISIBLE ? Color.WHITE : Color.TRANSPARENT); + caption.getBackground().setTint(visible ? Color.WHITE : Color.TRANSPARENT); } boolean isHandleMenuActive() { diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index 1a8b9540cbd0..2ac1dc0c4838 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -69,6 +69,9 @@ android_test { enabled: false, }, + platform_apis: true, + certificate: "platform", + aaptflags: [ "--extra-packages", "com.android.wm.shell.tests", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 707c04948b6f..08af3d3eecfe 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -16,8 +16,6 @@ package com.android.wm.shell.desktopmode; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -29,6 +27,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask; +import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask; +import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask; import static com.google.common.truth.Truth.assertThat; @@ -48,7 +49,6 @@ import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.window.DisplayAreaInfo; import android.window.TransitionRequestInfo; -import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction.Change; import android.window.WindowContainerTransaction.HierarchyOp; @@ -59,7 +59,6 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sysui.ShellController; @@ -355,7 +354,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testHandleTransitionRequest_taskOpen_returnsWct() { RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().mToken; + trigger.token = new MockToken().token(); trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); WindowContainerTransaction wct = mController.handleRequest( mock(IBinder.class), @@ -366,7 +365,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testHandleTransitionRequest_taskToFront_returnsWct() { RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().mToken; + trigger.token = new MockToken().token(); trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); WindowContainerTransaction wct = mController.handleRequest( mock(IBinder.class), @@ -381,40 +380,13 @@ public class DesktopModeControllerTest extends ShellTestCase { } private DisplayAreaInfo createMockDisplayArea() { - DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().mToken, + DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().token(), mContext.getDisplayId(), 0); when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId())) .thenReturn(displayAreaInfo); return displayAreaInfo; } - private RunningTaskInfo createFreeformTask() { - return new TestRunningTaskInfoBuilder() - .setToken(new MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FREEFORM) - .setLastActiveTime(100) - .build(); - } - - private RunningTaskInfo createFullscreenTask() { - return new TestRunningTaskInfoBuilder() - .setToken(new MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - .build(); - } - - private RunningTaskInfo createHomeTask() { - return new TestRunningTaskInfoBuilder() - .setToken(new MockToken().token()) - .setActivityType(ACTIVITY_TYPE_HOME) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - .build(); - } - private WindowContainerTransaction getDesktopModeSwitchTransaction() { ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass( WindowContainerTransaction.class); @@ -442,18 +414,4 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue(); } - private static class MockToken { - private final WindowContainerToken mToken; - private final IBinder mBinder; - - MockToken() { - mToken = mock(WindowContainerToken.class); - mBinder = mock(IBinder.class); - when(mToken.asBinder()).thenReturn(mBinder); - } - - WindowContainerToken token() { - return mToken; - } - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt new file mode 100644 index 000000000000..de2473b8deba --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -0,0 +1,281 @@ +/* + * 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.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.testing.AndroidTestingRunner +import android.window.WindowContainerTransaction +import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.ExtendedMockito.never +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.isNull +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopTasksControllerTest : ShellTestCase() { + + @Mock lateinit var testExecutor: ShellExecutor + @Mock lateinit var shellController: ShellController + @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer + @Mock lateinit var transitions: Transitions + + lateinit var mockitoSession: StaticMockitoSession + lateinit var controller: DesktopTasksController + lateinit var shellInit: ShellInit + lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + + // Mock running tasks are registered here so we can get the list from mock shell task organizer + private val runningTasks = mutableListOf<RunningTaskInfo>() + + @Before + fun setUp() { + mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking() + whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(true) + + shellInit = Mockito.spy(ShellInit(testExecutor)) + desktopModeTaskRepository = DesktopModeTaskRepository() + + whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } + + controller = createController() + + shellInit.init() + } + + private fun createController(): DesktopTasksController { + return DesktopTasksController( + context, + shellInit, + shellController, + shellTaskOrganizer, + transitions, + desktopModeTaskRepository, + TestShellExecutor() + ) + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + + runningTasks.clear() + } + + @Test + fun instantiate_addInitCallback() { + verify(shellInit).addInitCallback(any(), any<DesktopTasksController>()) + } + + @Test + fun instantiate_flagOff_doNotAddInitCallback() { + whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false) + clearInvocations(shellInit) + + createController() + + verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>()) + } + + @Test + fun showDesktopApps_allAppsInvisible_bringsToFront() { + val homeTask = setUpHomeTask() + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + markTaskHidden(task1) + markTaskHidden(task2) + + controller.showDesktopApps() + + val wct = getLatestWct() + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: home, task1, task2 + wct.assertReorderAt(index = 0, homeTask) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + fun showDesktopApps_appsAlreadyVisible_doesNothing() { + setUpHomeTask() + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + markTaskVisible(task1) + markTaskVisible(task2) + + controller.showDesktopApps() + + verifyWCTNotExecuted() + } + + @Test + fun showDesktopApps_someAppsInvisible_reordersAll() { + val homeTask = setUpHomeTask() + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + markTaskHidden(task1) + markTaskVisible(task2) + + controller.showDesktopApps() + + val wct = getLatestWct() + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: home, task1, task2 + wct.assertReorderAt(index = 0, homeTask) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + fun showDesktopApps_noActiveTasks_reorderHomeToTop() { + val homeTask = setUpHomeTask() + + controller.showDesktopApps() + + val wct = getLatestWct() + assertThat(wct.hierarchyOps).hasSize(1) + wct.assertReorderAt(index = 0, homeTask) + } + + @Test + fun moveToDesktop() { + val task = setUpFullscreenTask() + controller.moveToDesktop(task) + val wct = getLatestWct() + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FREEFORM) + } + + @Test + fun moveToDesktop_nonExistentTask_doesNothing() { + controller.moveToDesktop(999) + verifyWCTNotExecuted() + } + + @Test + fun moveToFullscreen() { + val task = setUpFreeformTask() + controller.moveToFullscreen(task) + val wct = getLatestWct() + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FULLSCREEN) + } + + @Test + fun moveToFullscreen_nonExistentTask_doesNothing() { + controller.moveToFullscreen(999) + verifyWCTNotExecuted() + } + + @Test + fun getTaskWindowingMode() { + val fullscreenTask = setUpFullscreenTask() + val freeformTask = setUpFreeformTask() + + assertThat(controller.getTaskWindowingMode(fullscreenTask.taskId)) + .isEqualTo(WINDOWING_MODE_FULLSCREEN) + assertThat(controller.getTaskWindowingMode(freeformTask.taskId)) + .isEqualTo(WINDOWING_MODE_FREEFORM) + assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED) + } + + private fun setUpFreeformTask(): RunningTaskInfo { + val task = createFreeformTask() + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + desktopModeTaskRepository.addActiveTask(task.taskId) + desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId) + runningTasks.add(task) + return task + } + + private fun setUpHomeTask(): RunningTaskInfo { + val task = createHomeTask() + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + runningTasks.add(task) + return task + } + + private fun setUpFullscreenTask(): RunningTaskInfo { + val task = createFullscreenTask() + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + runningTasks.add(task) + return task + } + + private fun markTaskVisible(task: RunningTaskInfo) { + desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = true) + } + + private fun markTaskHidden(task: RunningTaskInfo) { + desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false) + } + + private fun getLatestWct(): WindowContainerTransaction { + val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(transitions).startTransition(anyInt(), arg.capture(), isNull()) + } else { + verify(shellTaskOrganizer).applyTransaction(arg.capture()) + } + return arg.value + } + + private fun verifyWCTNotExecuted() { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(transitions, never()).startTransition(anyInt(), any(), isNull()) + } else { + verify(shellTaskOrganizer, never()).applyTransaction(any()) + } + } +} + +private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) { + assertWithMessage("WCT does not have a hierarchy operation at index $index") + .that(hierarchyOps.size) + .isGreaterThan(index) + val op = hierarchyOps[index] + assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER) + assertThat(op.container).isEqualTo(task.token.asBinder()) +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt new file mode 100644 index 000000000000..dc91d756842e --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.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.wm.shell.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import com.android.wm.shell.TestRunningTaskInfoBuilder + +class DesktopTestHelpers { + companion object { + /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */ + @JvmStatic + fun createFreeformTask(): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .setLastActiveTime(100) + .build() + } + + /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */ + @JvmStatic + fun createFullscreenTask(): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build() + } + + /** Create a new home task */ + @JvmStatic + fun createHomeTask(): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_HOME) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build() + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java new file mode 100644 index 000000000000..09d474d1f97c --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java @@ -0,0 +1,40 @@ +/* + * 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.desktopmode; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.IBinder; +import android.window.WindowContainerToken; + +/** + * {@link WindowContainerToken} wrapper that supports a mock binder + */ +class MockToken { + private final WindowContainerToken mToken; + + MockToken() { + mToken = mock(WindowContainerToken.class); + IBinder binder = mock(IBinder.class); + when(mToken.asBinder()).thenReturn(binder); + } + + WindowContainerToken token() { + return mToken; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java index 0dbf30d69f75..487583262011 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java @@ -48,6 +48,7 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; +import com.android.wm.shell.desktopmode.DesktopTasksController; import org.junit.Before; import org.junit.Test; @@ -74,6 +75,7 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { @Mock private DisplayController mDisplayController; @Mock private SyncTransactionQueue mSyncQueue; @Mock private DesktopModeController mDesktopModeController; + @Mock private DesktopTasksController mDesktopTasksController; @Mock private InputMonitor mInputMonitor; @Mock private InputManager mInputManager; @@ -95,6 +97,7 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { mDisplayController, mSyncQueue, Optional.of(mDesktopModeController), + Optional.of(mDesktopTasksController), mCaptionWindowDecorFactory, mMockInputMonitorFactory ); diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index 9f52bf18f4e3..308bb3eb723f 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -100,6 +100,8 @@ public class ImageWriter implements AutoCloseable { private final Object mListenerLock = new Object(); private OnImageReleasedListener mListener; private ListenerHandler mListenerHandler; + private final Object mCloseLock = new Object(); + private boolean mIsWriterValid = false; private long mNativeContext; private int mWidth; @@ -305,6 +307,8 @@ public class ImageWriter implements AutoCloseable { ImageUtils.getEstimatedNativeAllocBytes(mWidth, mHeight, useLegacyImageFormat ? imageFormat : hardwareBufferFormat, /*buffer count*/ 1); VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes); + + mIsWriterValid = true; } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, @@ -448,14 +452,17 @@ public class ImageWriter implements AutoCloseable { * @see Image#close */ public Image dequeueInputImage() { - if (mDequeuedImages.size() >= mMaxImages) { - throw new IllegalStateException("Already dequeued max number of Images " + mMaxImages); + synchronized (mCloseLock) { + if (mDequeuedImages.size() >= mMaxImages) { + throw new IllegalStateException( + "Already dequeued max number of Images " + mMaxImages); + } + WriterSurfaceImage newImage = new WriterSurfaceImage(this); + nativeDequeueInputImage(mNativeContext, newImage); + mDequeuedImages.add(newImage); + newImage.mIsImageValid = true; + return newImage; } - WriterSurfaceImage newImage = new WriterSurfaceImage(this); - nativeDequeueInputImage(mNativeContext, newImage); - mDequeuedImages.add(newImage); - newImage.mIsImageValid = true; - return newImage; } /** @@ -513,49 +520,53 @@ public class ImageWriter implements AutoCloseable { if (image == null) { throw new IllegalArgumentException("image shouldn't be null"); } - boolean ownedByMe = isImageOwnedByMe(image); - if (ownedByMe && !(((WriterSurfaceImage) image).mIsImageValid)) { - throw new IllegalStateException("Image from ImageWriter is invalid"); - } - - // For images from other components that have non-null owner, need to detach first, - // then attach. Images without owners must already be attachable. - if (!ownedByMe) { - if ((image.getOwner() instanceof ImageReader)) { - ImageReader prevOwner = (ImageReader) image.getOwner(); - prevOwner.detachImage(image); - } else if (image.getOwner() != null) { - throw new IllegalArgumentException("Only images from ImageReader can be queued to" - + " ImageWriter, other image source is not supported yet!"); + synchronized (mCloseLock) { + boolean ownedByMe = isImageOwnedByMe(image); + if (ownedByMe && !(((WriterSurfaceImage) image).mIsImageValid)) { + throw new IllegalStateException("Image from ImageWriter is invalid"); } - attachAndQueueInputImage(image); - // This clears the native reference held by the original owner. - // When this Image is detached later by this ImageWriter, the - // native memory won't be leaked. - image.close(); - return; - } + // For images from other components that have non-null owner, need to detach first, + // then attach. Images without owners must already be attachable. + if (!ownedByMe) { + if ((image.getOwner() instanceof ImageReader)) { + ImageReader prevOwner = (ImageReader) image.getOwner(); - Rect crop = image.getCropRect(); - nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), image.getDataSpace(), - crop.left, crop.top, crop.right, crop.bottom, image.getTransform(), - image.getScalingMode()); + prevOwner.detachImage(image); + } else if (image.getOwner() != null) { + throw new IllegalArgumentException( + "Only images from ImageReader can be queued to" + + " ImageWriter, other image source is not supported yet!"); + } - /** - * Only remove and cleanup the Images that are owned by this - * ImageWriter. Images detached from other owners are only temporarily - * owned by this ImageWriter and will be detached immediately after they - * are released by downstream consumers, so there is no need to keep - * track of them in mDequeuedImages. - */ - if (ownedByMe) { - mDequeuedImages.remove(image); - // Do not call close here, as close is essentially cancel image. - WriterSurfaceImage wi = (WriterSurfaceImage) image; - wi.clearSurfacePlanes(); - wi.mIsImageValid = false; + attachAndQueueInputImage(image); + // This clears the native reference held by the original owner. + // When this Image is detached later by this ImageWriter, the + // native memory won't be leaked. + image.close(); + return; + } + + Rect crop = image.getCropRect(); + nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), image.getDataSpace(), + crop.left, crop.top, crop.right, crop.bottom, image.getTransform(), + image.getScalingMode()); + + /** + * Only remove and cleanup the Images that are owned by this + * ImageWriter. Images detached from other owners are only temporarily + * owned by this ImageWriter and will be detached immediately after they + * are released by downstream consumers, so there is no need to keep + * track of them in mDequeuedImages. + */ + if (ownedByMe) { + mDequeuedImages.remove(image); + // Do not call close here, as close is essentially cancel image. + WriterSurfaceImage wi = (WriterSurfaceImage) image; + wi.clearSurfacePlanes(); + wi.mIsImageValid = false; + } } } @@ -691,17 +702,23 @@ public class ImageWriter implements AutoCloseable { */ @Override public void close() { - setOnImageReleasedListener(null, null); - for (Image image : mDequeuedImages) { - image.close(); - } - mDequeuedImages.clear(); - nativeClose(mNativeContext); - mNativeContext = 0; + synchronized (mCloseLock) { + if (!mIsWriterValid) { + return; + } + setOnImageReleasedListener(null, null); + for (Image image : mDequeuedImages) { + image.close(); + } + mDequeuedImages.clear(); + nativeClose(mNativeContext); + mNativeContext = 0; - if (mEstimatedNativeAllocBytes > 0) { - VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes); - mEstimatedNativeAllocBytes = 0; + if (mEstimatedNativeAllocBytes > 0) { + VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes); + mEstimatedNativeAllocBytes = 0; + } + mIsWriterValid = false; } } @@ -790,10 +807,16 @@ public class ImageWriter implements AutoCloseable { @Override public void handleMessage(Message msg) { OnImageReleasedListener listener; - synchronized (mListenerLock) { + boolean isWriterValid; + synchronized (ImageWriter.this.mListenerLock) { listener = mListener; } - if (listener != null) { + // Check to make sure we don't accidentally queue images after the writer is + // closed or closing + synchronized (ImageWriter.this.mCloseLock) { + isWriterValid = ImageWriter.this.mIsWriterValid; + } + if (listener != null && isWriterValid) { listener.onImageReleased(ImageWriter.this); } } @@ -813,10 +836,14 @@ public class ImageWriter implements AutoCloseable { } final Handler handler; + final boolean isWriterValid; synchronized (iw.mListenerLock) { handler = iw.mListenerHandler; } - if (handler != null) { + synchronized (iw.mCloseLock) { + isWriterValid = iw.mIsWriterValid; + } + if (handler != null && isWriterValid) { handler.sendEmptyMessage(0); } } @@ -1050,6 +1077,9 @@ public class ImageWriter implements AutoCloseable { private int mTransform = 0; //Default no transform private int mScalingMode = 0; //Default frozen scaling mode + private final Object mCloseLock = new Object(); // lock to protect against multiple + // simultaneous calls to close() + public WriterSurfaceImage(ImageWriter writer) { mOwner = writer; mWidth = writer.mWidth; @@ -1192,8 +1222,10 @@ public class ImageWriter implements AutoCloseable { @Override public void close() { - if (mIsImageValid) { - getOwner().abortImage(this); + synchronized (mCloseLock) { + if (mIsImageValid) { + getOwner().abortImage(this); + } } } diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml index 3ab43f13713e..c11e2b72399b 100644 --- a/packages/PrintSpooler/res/values-te/strings.xml +++ b/packages/PrintSpooler/res/values-te/strings.xml @@ -49,10 +49,10 @@ <string name="print_options_collapsed" msgid="7455930445670414332">"ముద్రణ ఎంపికలు కుదించబడ్డాయి"</string> <string name="search" msgid="5421724265322228497">"సెర్చ్"</string> <string name="all_printers_label" msgid="3178848870161526399">"అన్ని ప్రింటర్లు"</string> - <string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించు"</string> + <string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించండి"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"సెర్చ్ బాక్స్ చూపబడింది"</string> <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"సెర్చ్ బాక్స్ దాచబడింది"</string> - <string name="print_add_printer" msgid="1088656468360653455">"ప్రింటర్ను జోడించు"</string> + <string name="print_add_printer" msgid="1088656468360653455">"ప్రింటర్ను జోడించండి"</string> <string name="print_select_printer" msgid="7388760939873368698">"ప్రింటర్ను ఎంచుకోండి"</string> <string name="print_forget_printer" msgid="5035287497291910766">"ప్రింటర్ను విస్మరించు"</string> <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868"> diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml index 0de7be0a5a08..5d6c343c1fbc 100644 --- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml +++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml @@ -40,6 +40,7 @@ <color name="settingslib_color_grey800">#3c4043</color> <color name="settingslib_color_grey700">#5f6368</color> <color name="settingslib_color_grey600">#80868b</color> + <color name="settingslib_color_grey500">#9AA0A6</color> <color name="settingslib_color_grey400">#bdc1c6</color> <color name="settingslib_color_grey300">#dadce0</color> <color name="settingslib_color_grey200">#e8eaed</color> diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java index 93b6acc9e160..5e2c43735361 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java @@ -40,10 +40,13 @@ public class LottieColorUtils { HashMap<String, Integer> map = new HashMap<>(); map.put( ".grey600", - R.color.settingslib_color_grey300); + R.color.settingslib_color_grey400); + map.put( + ".grey700", + R.color.settingslib_color_grey500); map.put( ".grey800", - R.color.settingslib_color_grey200); + R.color.settingslib_color_grey300); map.put( ".grey900", R.color.settingslib_color_grey50); diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index adbdf228aea1..ad4c32e3af8c 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -479,8 +479,7 @@ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string> <!-- no translation found for power_charging_limited (6732738149313642521) --> <skip /> - <!-- no translation found for power_charging_future_paused (6829683663982987290) --> - <skip /> + <string name="power_charging_future_paused" msgid="6829683663982987290">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስከ <xliff:g id="DOCK_DEFENDER_THRESHOLD">%2$s</xliff:g> ድረስ ኃይል መሙላት"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index ca2f14327df6..730247dde2d5 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -342,7 +342,7 @@ <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string> - <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ యాక్సెస్ను అందించే టెర్మినల్ యాప్ను ప్రారంభించు"</string> + <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ యాక్సెస్ను అందించే టెర్మినల్ యాప్ను ప్రారంభించండి"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP చెకింగ్"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP తనిఖీ ప్రవర్తనను సెట్ చేయండి"</string> <string name="debug_debugging_category" msgid="535341063709248842">"డీబగ్గింగ్"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index d1ac7d00c1cf..cf372057765c 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1125,9 +1125,9 @@ <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration --> <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string> <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited --> - <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging paused</string> + <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string> <!-- [CHAR_LIMIT=80] Label for battery charging future pause --> - <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging to <xliff:g id="dock_defender_threshold">%2$s</xliff:g></string> + <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 46a94fdfcd17..99b15db780dc 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -165,6 +165,9 @@ <!-- Default for Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS --> <bool name="def_lock_screen_allow_private_notifications">true</bool> + <!-- Default for Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS --> + <bool name="def_lock_screen_show_only_unseen_notifications">false</bool> + <!-- Default for Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 1==on --> <integer name="def_heads_up_enabled">1</integer> diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 92a938cb0b47..1b0b6b4ff796 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -146,6 +146,7 @@ public class SecureSettings { Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, Settings.Secure.SHOW_NOTIFICATION_SNOOZE, Settings.Secure.NOTIFICATION_HISTORY_ENABLED, Settings.Secure.ZEN_DURATION, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index eabf4cc8edfd..4fa490ffaed5 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -219,6 +219,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SHOW_NOTIFICATION_SNOOZE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.NOTIFICATION_HISTORY_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ZEN_DURATION, ANY_INTEGER_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index ded7e785ca75..84a559391343 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3631,7 +3631,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 211; + private static final int SETTINGS_VERSION = 212; private final int mUserId; @@ -5527,6 +5527,26 @@ public class SettingsProvider extends ContentProvider { } currentVersion = 211; } + if (currentVersion == 211) { + // Version 211: Set default value for + // Secure#LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting lockScreenUnseenSetting = secureSettings + .getSettingLocked(Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS); + if (lockScreenUnseenSetting.isNull()) { + final boolean defSetting = getContext().getResources() + .getBoolean(R.bool.def_lock_screen_show_only_unseen_notifications); + secureSettings.insertSettingOverrideableByRestoreLocked( + Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + defSetting ? "1" : "0", + null /* tag */, + true /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + + currentVersion = 212; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SoundPicker/res/values-te/strings.xml b/packages/SoundPicker/res/values-te/strings.xml index feaf4c83b51e..2d03ac0e844c 100644 --- a/packages/SoundPicker/res/values-te/strings.xml +++ b/packages/SoundPicker/res/values-te/strings.xml @@ -19,9 +19,9 @@ <string name="ringtone_default" msgid="798836092118824500">"ఆటోమేటిక్ రింగ్టోన్"</string> <string name="notification_sound_default" msgid="8133121186242636840">"నోటిఫికేషన్ ఆటోమేటిక్ సౌండ్"</string> <string name="alarm_sound_default" msgid="4787646764557462649">"అలారం ఆటోమేటిక్ సౌండ్"</string> - <string name="add_ringtone_text" msgid="6642389991738337529">"రింగ్టోన్ను జోడించు"</string> - <string name="add_alarm_text" msgid="3545497316166999225">"అలారాన్ని జోడించు"</string> - <string name="add_notification_text" msgid="4431129543300614788">"నోటిఫికేషన్ని జోడించు"</string> + <string name="add_ringtone_text" msgid="6642389991738337529">"రింగ్టోన్ను జోడించండి"</string> + <string name="add_alarm_text" msgid="3545497316166999225">"అలారాన్ని జోడించండి"</string> + <string name="add_notification_text" msgid="4431129543300614788">"నోటిఫికేషన్ని జోడించండి"</string> <string name="delete_ringtone_text" msgid="201443984070732499">"తొలగించండి"</string> <string name="unable_to_add_ringtone" msgid="4583511263449467326">"అనుకూల రింగ్టోన్ను జోడించలేకపోయింది"</string> <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"అనుకూల రింగ్టోన్ను తొలగించలేకపోయింది"</string> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4f08a30ed630..2f5b42ffe641 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -886,7 +886,7 @@ android:showForAllUsers="true" android:finishOnTaskLaunch="true" android:launchMode="singleInstance" - android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" android:visibleToInstantApps="true"> </activity> 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 ebabdf571dfd..fe349f21e36e 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -49,12 +49,12 @@ private const val TAG = "ActivityLaunchAnimator" */ class ActivityLaunchAnimator( /** The animator used when animating a View into an app. */ - private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), + private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR, /** The animator used when animating a Dialog into an app. */ // TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to // TIMINGS.contentBeforeFadeOutDuration. - private val dialogToAppAnimator: LaunchAnimator = LaunchAnimator(DIALOG_TIMINGS, INTERPOLATORS) + private val dialogToAppAnimator: LaunchAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR ) { companion object { /** The timings when animating a View into an app. */ @@ -85,6 +85,9 @@ class ActivityLaunchAnimator( contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f) ) + private val DEFAULT_LAUNCH_ANIMATOR = LaunchAnimator(TIMINGS, INTERPOLATORS) + private val DEFAULT_DIALOG_TO_APP_ANIMATOR = LaunchAnimator(DIALOG_TIMINGS, INTERPOLATORS) + /** Durations & interpolators for the navigation bar fading in & out. */ private const val ANIMATION_DURATION_NAV_FADE_IN = 266L private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L @@ -117,6 +120,22 @@ class ActivityLaunchAnimator( /** The set of [Listener] that should be notified of any animation started by this animator. */ private val listeners = LinkedHashSet<Listener>() + /** Top-level listener that can be used to notify all registered [listeners]. */ + private val lifecycleListener = + object : Listener { + override fun onLaunchAnimationStart() { + listeners.forEach { it.onLaunchAnimationStart() } + } + + override fun onLaunchAnimationEnd() { + listeners.forEach { it.onLaunchAnimationEnd() } + } + + override fun onLaunchAnimationProgress(linearProgress: Float) { + listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } + } + } + /** * Start an intent and animate the opening window. The intent will be started by running * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch @@ -156,7 +175,7 @@ class ActivityLaunchAnimator( ?: throw IllegalStateException( "ActivityLaunchAnimator.callback must be set before using this animator" ) - val runner = Runner(controller) + val runner = createRunner(controller) val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the @@ -256,7 +275,18 @@ class ActivityLaunchAnimator( } /** Create a new animation [Runner] controlled by [controller]. */ - @VisibleForTesting fun createRunner(controller: Controller): Runner = Runner(controller) + @VisibleForTesting + fun createRunner(controller: Controller): Runner { + // Make sure we use the modified timings when animating a dialog into an app. + val launchAnimator = + if (controller.isDialogLaunch) { + dialogToAppAnimator + } else { + launchAnimator + } + + return Runner(controller, callback!!, launchAnimator, lifecycleListener) + } interface PendingIntentStarter { /** @@ -353,14 +383,20 @@ class ActivityLaunchAnimator( * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called * before the cancellation. * - * If this launch animation affected the occlusion state of the keyguard, WM will provide - * us with [newKeyguardOccludedState] so that we can set the occluded state appropriately. + * If this launch animation affected the occlusion state of the keyguard, WM will provide us + * with [newKeyguardOccludedState] so that we can set the occluded state appropriately. */ fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {} } - @VisibleForTesting - inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() { + class Runner( + private val controller: Controller, + private val callback: Callback, + /** The animator to use to animate the window launch. */ + private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR, + /** Listener for animation lifecycle events. */ + private val listener: Listener? = null + ) : IRemoteAnimationRunner.Stub() { private val launchContainer = controller.launchContainer private val context = launchContainer.context private val transactionApplierView = @@ -448,18 +484,9 @@ class ActivityLaunchAnimator( left = windowBounds.left, right = windowBounds.right ) - val callback = this@ActivityLaunchAnimator.callback!! val windowBackgroundColor = window.taskInfo?.let { callback.getBackgroundColor(it) } ?: window.backgroundColor - // Make sure we use the modified timings when animating a dialog into an app. - val launchAnimator = - if (controller.isDialogLaunch) { - dialogToAppAnimator - } else { - launchAnimator - } - // TODO(b/184121838): We should somehow get the top and bottom radius of the window // instead of recomputing isExpandingFullyAbove here. val isExpandingFullyAbove = @@ -483,12 +510,12 @@ class ActivityLaunchAnimator( val controller = object : Controller by delegate { override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { - listeners.forEach { it.onLaunchAnimationStart() } + listener?.onLaunchAnimationStart() delegate.onLaunchAnimationStart(isExpandingFullyAbove) } override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { - listeners.forEach { it.onLaunchAnimationEnd() } + listener?.onLaunchAnimationEnd() iCallback?.invoke() delegate.onLaunchAnimationEnd(isExpandingFullyAbove) } @@ -505,7 +532,7 @@ class ActivityLaunchAnimator( } navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } - listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } + listener?.onLaunchAnimationProgress(linearProgress) delegate.onLaunchAnimationProgress(state, progress, linearProgress) } } diff --git a/packages/SystemUI/compose/features/AndroidManifest.xml b/packages/SystemUI/compose/features/AndroidManifest.xml index 278a89f7dba3..c1a9ec55c227 100644 --- a/packages/SystemUI/compose/features/AndroidManifest.xml +++ b/packages/SystemUI/compose/features/AndroidManifest.xml @@ -16,43 +16,6 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.systemui.compose.features"> - <application - android:name="android.app.Application" - android:appComponentFactory="androidx.core.app.AppComponentFactory" - tools:replace="android:name,android:appComponentFactory"> - <!-- Disable providers from SystemUI --> - <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider" - android:authorities="com.android.systemui.test.keyguard.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle" - android:authorities="com.android.systemui.test.keyguard.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider" - android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.systemui.test.keyguard.clock.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.systemui.people.PeopleProvider" - android:authorities="com.android.systemui.test.people.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="androidx.core.content.FileProvider" - android:authorities="com.android.systemui.test.fileprovider.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove"/> - </application> + </manifest> diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml index 5e54c1f353d2..2fa475d63607 100644 --- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml +++ b/packages/SystemUI/compose/features/tests/AndroidManifest.xml @@ -15,10 +15,46 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="com.android.systemui.compose.features.tests" > - <application> + <application + android:name="android.app.Application" + android:appComponentFactory="androidx.core.app.AppComponentFactory" + tools:replace="android:name,android:appComponentFactory"> <uses-library android:name="android.test.runner" /> + + <!-- Disable providers from SystemUI --> + <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider" + android:authorities="com.android.systemui.test.keyguard.disabled" + android:enabled="false" + tools:replace="android:authorities" + tools:node="remove" /> + <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle" + android:authorities="com.android.systemui.test.keyguard.disabled" + android:enabled="false" + tools:replace="android:authorities" + tools:node="remove" /> + <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider" + android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled" + android:enabled="false" + tools:replace="android:authorities" + tools:node="remove" /> + <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" + android:authorities="com.android.systemui.test.keyguard.clock.disabled" + android:enabled="false" + tools:replace="android:authorities" + tools:node="remove" /> + <provider android:name="com.android.systemui.people.PeopleProvider" + android:authorities="com.android.systemui.test.people.disabled" + android:enabled="false" + tools:replace="android:authorities" + tools:node="remove" /> + <provider android:name="androidx.core.content.FileProvider" + android:authorities="com.android.systemui.test.fileprovider.disabled" + android:enabled="false" + tools:replace="android:authorities" + tools:node="remove"/> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index 20d32f187d2e..12f96739071c 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -84,7 +84,7 @@ <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Perangkat dikunci oleh admin"</string> <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Perangkat dikunci secara manual"</string> <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string> - <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Untuk pakai Face Unlock, beri akses kamera di Setelan"</string> + <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Untuk pakai Buka dengan Wajah, beri akses kamera di Setelan"</string> <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Masukkan PIN SIM. Tersisa # percobaan lagi sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.}other{Masukkan PIN SIM. Tersisa # percobaan lagi.}}"</string> <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa # percobaan lagi sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.}other{SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa # percobaan lagi sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.}}"</string> <string name="clock_title_default" msgid="6342735240617459864">"Default"</string> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index da485a99c29b..f5221670f23c 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -53,7 +53,7 @@ <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string> <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that charging is temporarily limited. --> - <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging paused to protect battery</string> + <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging optimized to protect battery</string> <!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock. This is shown in small font at the bottom. --> <string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 16c615b9059d..fe6aebca7e21 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hou op uitsaai"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Beskikbare toestelle vir oudio-uitsette."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera en mikrofoon is af"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# kennisgewing}other{# kennisgewings}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Neem notas"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitsaai"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hou op om <xliff:g id="APP_NAME">%1$s</xliff:g> uit te saai?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"As jy <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitsaai of die uitvoer verander, sal jou huidige uitsending stop"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Saai <xliff:g id="SWITCHAPP">%1$s</xliff:g> uit"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Verander uitvoer"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Onbekend"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE. d MMM."</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Maak <xliff:g id="APPNAME">%1$s</xliff:g> oop"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 966701a26ff5..93d63791a710 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -998,7 +998,6 @@ <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> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ይክፈቱ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index eae26f050e90..dad19abb1d54 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"إيقاف البث"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"الأجهزة المتاحة لإخراج الصوت"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"مستوى الصوت"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"كيفية عمل البث"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"البث"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"يمكن للأشخاص القريبين منك الذين لديهم أجهزة متوافقة تتضمّن بلوتوث الاستماع إلى الوسائط التي تبثها."</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"الكاميرا والميكروفون غير مفعّلين."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{إشعار واحد}zero{# إشعار}two{إشعاران}few{# إشعارات}many{# إشعارًا}other{# إشعار}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"تدوين الملاحظات"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"البث"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"هل تريد إيقاف بث تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>؟"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"إذا أجريت بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g> أو غيَّرت جهاز الإخراج، سيتوقَف البث الحالي."</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"تغيير جهاز الإخراج"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"غير معروف"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE، d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"فتح \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 6fc82be36941..9c8aed6dc1eb 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"কাষ্ট বন্ধ কৰক"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"অডিঅ\' আউটপুটৰ বাবে উপলব্ধ ডিভাইচ।"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ভলিউম"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"সম্প্ৰচাৰ কৰাটোৱে কেনেকৈ কাম কৰে"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্ৰচাৰ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"সমিল ব্লুটুথ ডিভাইচৰ সৈতে আপোনাৰ নিকটৱৰ্তী স্থানত থকা লোকসকলে আপুনি সম্প্ৰচাৰ কৰা মিডিয়াটো শুনিব পাৰে"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"কেমেৰা আৰু মাইক অফ হৈ আছে"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# টা জাননী}one{# টা জাননী}other{# টা জাননী}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"টোকাগ্ৰহণ"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"সম্প্ৰচাৰণ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰা বন্ধ কৰিবনে?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"যদি আপুনি <xliff:g id="SWITCHAPP">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰে অথবা আউটপুট সলনি কৰে, তেন্তে, আপোনাৰ বৰ্তমানৰ সম্প্ৰচাৰ বন্ধ হৈ যাব"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্ৰচাৰ কৰক"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"আউটপুট সলনি কৰক"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"অজ্ঞাত"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> খোলক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index e4e031a417cb..8870b6167055 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayımı dayandırın"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Audio çıxış üçün əlçatan cihazlar."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Səs"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə işləyir"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Yayım"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Uyğun Bluetooth cihazları olan yaxınlığınızdakı insanlar yayımladığınız medianı dinləyə bilər"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera və mikrofon deaktivdir"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildiriş}other{# bildiriş}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Qeyd tutma"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayım"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinin yayımlanması dayandırılsın?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlasanız və ya nəticəni dəyişsəniz, cari yayımınız dayandırılacaq"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlayın"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Nəticəni dəyişdirin"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Naməlum"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"HHH, AAA g"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:dd"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ss:dd"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> tətbiqini açın"</string> @@ -1011,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Daha yaxşı selfi üçün ön displeyə çevrilsin?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Daha yüksək ayırdetmə dəqiqliyi ilə daha geniş şəkil üçün arxaya baxan kameradan istifadə edin."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Bu ekran deaktiv ediləcək"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Qatlana bilən cihaz açılır"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Qatlana bilən cihaz fırladılır"</string> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index bd964f955a00..35cbed93d906 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitujte aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promenite izlaz"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"DDD, d. MMM"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:min"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"č:min"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvorite: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 31c9bcc49ebb..c653c7a4857c 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -723,7 +723,7 @@ <string name="instant_apps_title" msgid="8942706782103036910">"Праграма \"<xliff:g id="APP">%1$s</xliff:g>\" запушчана"</string> <string name="instant_apps_message" msgid="6112428971833011754">"Праграма адкрыта без усталёўкі."</string> <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Праграма адкрыта без усталёўкі. Націсніце, каб даведацца больш."</string> - <string name="app_info" msgid="5153758994129963243">"Звесткі пра праграму"</string> + <string name="app_info" msgid="5153758994129963243">"Звесткі аб праграме"</string> <string name="go_to_web" msgid="636673528981366511">"Перайсці ў браўзер"</string> <string name="mobile_data" msgid="4564407557775397216">"Маб. перадача даных"</string> <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string> @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спыніць трансляцыю"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Даступныя прылады для вываду аўдыя."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучнасць"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як адбываецца трансляцыя"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляцыя"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Людзі паблізу, у якіх ёсць прылады з Bluetooth, змогуць праслухваць мультымедыйнае змесціва, якое вы трансліруеце"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера і мікрафон выключаны"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# апавяшчэнне}one{# апавяшчэнне}few{# апавяшчэнні}many{# апавяшчэнняў}other{# апавяшчэння}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Стварэнне нататак"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Перадача даных"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Спыніць трансляцыю праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Пры пераключэнні на праграму \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" ці змяненні вываду бягучая трансляцыя спыняецца"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Трансляцыя праграмы \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\""</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Змяненне вываду"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Невядома"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Адкрыць праграму \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index c2860e95a807..cd2ad4d1c590 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спиране на предаването"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Налични устройства за аудиоизход."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Сила на звука"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работи предаването"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Предаване"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Хората в близост със съвместими устройства с Bluetooth могат да слушат мултимедията, която предавате"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камерата и микрофонът са изключени"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известие}other{# известия}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Водене на бележки"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Излъчване"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се спре ли предаването на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако предавате <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените изхода, текущото ви предаване ще бъде прекратено"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Предаване на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Промяна на изхода"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Неизвестно"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отваряне на <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 42fc489dff54..4d5e2a3f4945 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -993,7 +993,6 @@ <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> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> খুলুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 62f0c5f9c507..cb4b2a563d31 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -986,14 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera i mikrofon su isključeni"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavještenje}one{# obavještenje}few{# obavještenja}other{# obavještenja}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pisanje bilježaka"</string> + <string name="note_task_button_label" msgid="8718616095800343136">"Pisanje bilješki"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, trenutno emitiranje će se zaustaviti"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitiraj aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promijeni izlaz"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"DDD, 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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvori aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Obrnuti na prednji ekran radi boljeg selfija?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Koristite stražnju kameru za širu fotografiju veće rezolucije."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ekran će se isključiti"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rasklopljen sklopivi uređaj"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Okretanje sklopivog uređaja sa svih strana"</string> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 8f591bcb991a..68fa2dcff6f6 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Atura l\'emissió"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositius disponibles per a la sortida d\'àudio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emet"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les persones properes amb dispositius Bluetooth compatibles poden escoltar el contingut multimèdia que emets"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Càmera i micròfon desactivats"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificació}many{# notifications}other{# notificacions}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Presa de notes"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"S\'està emetent"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vols deixar d\'emetre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emets <xliff:g id="SWITCHAPP">%1$s</xliff:g> o canvies la sortida, l\'emissió actual s\'aturarà"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emet <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Canvia la sortida"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconeguda"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Obre <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index c417d4460d31..6eb360bc9fb9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zastavit odesílání"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dostupná zařízení pro zvukový výstup."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hlasitost"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Vysílání"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lidé ve vašem okolí s kompatibilními zařízeními Bluetooth mohou poslouchat média, která vysíláte"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparát a mikrofon jsou vypnuté"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# oznámení}few{# oznámení}many{# oznámení}other{# oznámení}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g> <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Poznámky"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysílání"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zastavit vysílání v aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Pokud budete vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g> nebo změníte výstup, aktuální vysílání se zastaví"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Změna výstupu"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznámé"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMMM"</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">"H:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otevřít <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index d4277a7dea85..3ba90870e93c 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Udsend <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Skift output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ukendt"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMM"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"tt.mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk.mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Åbn <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index d1c88fb65c9c..c2d868c9b874 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Streaming beenden"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Für die Audioausgabe verfügbare Geräte."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lautstärke"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Nachricht an alle"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personen, die in der Nähe sind und kompatible Bluetooth-Geräten haben, können sich die Medien anhören, die du per Nachricht an alle sendest"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera und Mikrofon ausgeschaltet"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# Benachrichtigung}other{# Benachrichtigungen}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Notizen machen"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Übertragung läuft"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ausgabe ändern"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unbekannt"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> öffnen"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 84e18f0596ed..fd694885f0f0 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -993,7 +993,6 @@ <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> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"ΗΗΗ, ΜΜΜ η"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ώ:λλ"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:λλ"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Άνοιγμα <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Αναστροφή στην μπροστ. οθόνη για καλύτερη selfie;"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Χρησιμοποιήστε την πίσω κάμερα για να βγάλετε μια φωτογραφία με μεγαλύτερο εύρος και υψηλότερη ανάλυση."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Αυτή η οθόνη θα απενεργοποιηθεί"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Αναδιπλούμενη συσκευή που ξεδιπλώνει"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Αναδιπλούμενη συσκευή που διπλώνει"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 133bb64ef08a..244865b79111 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 4caf4e042258..5cc99f97c172 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 133bb64ef08a..244865b79111 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 133bb64ef08a..244865b79111 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index abb548b4ad6a..cb79d1134512 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930">""<b>"✱ This screen will turn off"</b>""</string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 3527aaf4cee5..b91d1d62b398 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Detener transmisión"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponibles para salida de audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisión"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que transmites"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están apagados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}many{# notificaciones}other{# notificaciones}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Tomar notas"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitiendo"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Quieres dejar de transmitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si transmites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu transmisión actual se detendrá"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambia la salida"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconocido"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index dcc52b558a74..e30b0e9fb804 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dejar de enviar contenido"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponibles para la salida de audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emisión"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que emites"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están desactivados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}many{# notificaciones}other{# notificaciones}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Tomar notas"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiendo"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Dejar de emitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu emisión actual se detendrá"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambiar salida"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconocido"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index afba616a85bd..cd1445b5b4df 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lõpeta ülekanne"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Saadaolevad seadmed heli esitamiseks."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Helitugevus"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Ülekanne"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Teie läheduses olevad inimesed, kellel on ühilduvad Bluetooth-seadmed, saavad kuulata teie ülekantavat meediat"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kaamera ja mikrofon on välja lülitatud"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# märguanne}other{# märguannet}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Märkmete tegemine"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Edastamine"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Kas peatada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> ülekandmine?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kui kannate rakendust <xliff:g id="SWITCHAPP">%1$s</xliff:g> üle või muudate väljundit, peatatakse teie praegune ülekanne"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Rakenduse <xliff:g id="SWITCHAPP">%1$s</xliff:g> ülekandmine"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Väljundi muutmine"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tundmatu"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ava <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 9ded73a98c26..f9508d17106d 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Gelditu igorpena"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Audio-irteerarako gailu erabilgarriak."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Bolumena"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth bidezko gailu bateragarriak dituzten inguruko pertsonek iragartzen ari zaren multimedia-edukia entzun dezakete"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera eta mikrofonoa desaktibatuta daude"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# jakinarazpen}other{# jakinarazpen}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Oharrak idaztea"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Igortzen"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren audioa igortzeari utzi nahi diozu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa igortzen baduzu, edo audio-irteera aldatzen baduzu, une hartako igorpena eten egingo da"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Igorri <xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Aldatu audio-irteera"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ezezaguna"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ireki <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index c93803e96ce5..bb195ce9d659 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"توقف پخش محتوا"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"دستگاههای دردسترس برای خروجی صدا."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"میزان صدا"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همهفرتستی چطور کار میکند"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"همهفرستی"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"افرادی که در اطرافتان دستگاههای Bluetooth سازگار دارند میتوانند به رسانهای که همهفرستی میکنید گوش کنند"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"دوربین و میکروفون خاموش هستند"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اعلان}one{# اعلان}other{# اعلان}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"یادداشتبرداری"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"همهفرستی"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"همهفرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همهفرستی کنید یا خروجی را تغییر دهید، همهفرستی کنونی متوقف خواهد شد"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"همهفرستی <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"تغییر خروجی"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"نامشخص"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"باز کردن <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index d3b2da3577c0..0783c15871e9 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lopeta striimaus"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Käytettävissä olevat audiolaitteet"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Äänenvoimakkuus"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Lähetys"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lähistöllä olevat ihmiset, joilla on yhteensopiva Bluetooth-laite, voivat kuunnella lähettämääsi mediaa"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera ja mikrofoni ovat pois päältä"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ilmoitus}other{# ilmoitusta}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Muistiinpanojen tekeminen"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Lähettää"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jos lähetät <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta tai muutat ulostuloa, nykyinen lähetyksesi loppuu"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Lähetä <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Muuta ulostuloa"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tuntematon"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"VKP, KKK p"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Avaa <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 0300e9cb4c78..15a57b0a1b23 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Appareils disponibles pour la sortie audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Diffusion"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité disposant d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"L\'appareil photo et le micro sont désactivés"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}many{# de notifications}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Prise de note"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion en cours…"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou changez la sortie, votre diffusion actuelle s\'arrêtera"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Changer la sortie"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Inconnue"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ouvrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index c1d58ebcaa5a..a1c6e36b5a25 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Appareils disponibles pour la sortie audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Appareil photo et micro désactivés"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}many{# notifications}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Prendre des notes"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion…"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou que vous modifiez le résultat, votre annonce actuelle s\'arrêtera"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Modifier le résultat"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Inconnue"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM j"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ouvrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index c1daa9863b60..60d2e0a3ee2f 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Deter emisión"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos dispoñibles para a saída de audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Difusión"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As persoas que estean preto de ti e que dispoñan de dispositivos Bluetooth compatibles poden escoitar o contido multimedia que difundas"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A cámara e o micrófono están desactivados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificacións}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Toma de notas"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Difusión"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Queres deixar de emitir contido a través de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se emites contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou cambias de saída, a emisión en curso deterase"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitir contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambiar de saída"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Descoñecida"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 6e306c826166..d9572efc160f 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -993,7 +993,6 @@ <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> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ખોલો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 5dba4665cac8..6f34cff12d17 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्टिंग करना रोकें"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ऑडियो आउटपुट के लिए उपलब्ध डिवाइस."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"वॉल्यूम"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्ट करने की सुविधा कैसे काम करती है"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करें"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"आपके आस-पास मौजूद लोग, ब्रॉडकास्ट किए जा रहे मीडिया को सुन सकते हैं. हालांकि, इसके लिए उनके पास ऐसे ब्लूटूथ डिवाइस होने चाहिए जिन पर मीडिया चलाया जा सके"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"कैमरा और माइक बंद हैं"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}one{# सूचना}other{# सूचनाएं}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"नोट बनाएं"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट ऐप्लिकेशन"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट शुरू करने पर या आउटपुट बदलने पर, आपका मौजूदा ब्रॉडकास्ट बंद हो जाएगा"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट करें"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपुट बदलें"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"कोई जानकारी नहीं"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> खोलें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 13f43be0e650..7e7a0d9e343d 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitiranje aplikacije <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promjena izlaza"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE., d. MMM."</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvorite <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Prebaciti na prednji zaslon za bolji selfie?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Upotrijebite stražnji fotoaparat za širu fotografiju s višom razlučivošću."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj će se zaslon isključiti"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rasklopljen sklopivi uređaj"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Okretanje sklopivog uređaja sa svih strana"</string> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 7c816c739217..9624acd79847 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Átküldés leállítása"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Rendelkezésre álló eszközök a hangkimenethez."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hangerő"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés működése"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Közvetítés"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"A közelben tartózkodó, kompatibilis Bluetooth-eszközzel rendelkező személyek meghallgathatják az Ön közvetített médiatartalmait"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A kamera és a mikrofon ki vannak kapcsolva"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# értesítés}other{# értesítés}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Jegyzetelés"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sugárzás"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Leállítja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> közvetítését?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"A(z) <xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése vagy a kimenet módosítása esetén a jelenlegi közvetítés leáll"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Kimenet módosítása"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ismeretlen"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, HHH n"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ó:pp"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"óó:pp"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> megnyitása"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index ea6023734fba..61357e51c351 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Կանգնեցնել հեռարձակումը"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Հասանելի սարքեր ձայնի արտածման համար։"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ձայնի ուժգնություն"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ինչպես է աշխատում հեռարձակումը"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Հեռարձակում"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ձեր մոտակայքում գտնվող՝ համատեղելի Bluetooth սարքերով մարդիկ կարող են լսել մեդիա ֆայլերը, որոնք դուք հեռարձակում եք։"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Տեսախցիկը և խոսափողն անջատված են"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ծանուցում}one{# ծանուցում}other{# ծանուցում}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Նշումների ստեղծում"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Հեռարձակում"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Կանգնեցնել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի հեռարձակումը"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Եթե հեռարձակեք <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը կամ փոխեք աուդիո ելքը, ձեր ընթացիկ հեռարձակումը կկանգնեցվի։"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Հեռարձակել <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Փոխել աուդիո ելքը"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Անհայտ"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Բացել <xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 3cdb26e4ed5e..84ae4d44d4a2 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hentikan transmisi"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Perangkat yang tersedia untuk output audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Siaran"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang di dekat Anda dengan perangkat Bluetooth yang kompatibel dapat mendengarkan media yang sedang Anda siarkan"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dan mikrofon nonaktif"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikasi}other{# notifikasi}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Pembuatan catatan"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika Anda menyiarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau mengubah output, siaran saat ini akan dihentikan"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ubah output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tidak diketahui"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buka <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 7c715382c008..85f7b721fc3a 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stöðva útsendingu"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Tæki í boði fyrir hljóðúttak."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hljóðstyrkur"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Útsending"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Fólk nálægt þér með samhæf Bluetooth-tæki getur hlustað á efnið sem þú sendir út"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Slökkt á myndavél og hljóðnema"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# tilkynning}one{# tilkynning}other{# tilkynningar}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Glósugerð"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Útsending í gangi"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hætta að senda út <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ef þú sendir út <xliff:g id="SWITCHAPP">%1$s</xliff:g> eða skiptir um úttak lýkur yfirstandandi útsendingu"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Senda út <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Skipta um úttak"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Óþekkt"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"k:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Opna <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index f6c83543b2ff..0ca503df4ae0 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Interrompi trasmissione"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivi disponibili per l\'uscita audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Annuncio"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Le persone vicine a te che hanno dispositivi Bluetooth compatibili possono ascoltare i contenuti multimediali che stai trasmettendo"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotocamera e microfono non attivi"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifica}many{# notifiche}other{# notifiche}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Aggiunta di note"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Trasmissione in corso…"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vuoi interrompere la trasmissione dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambi l\'uscita, la trasmissione attuale viene interrotta"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambia uscita"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM g"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Apri <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 8e12a0b92f24..5df872b350c7 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"עצירת ההעברה (casting)"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"מכשירים זמינים לפלט אודיו."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"עוצמת הקול"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"הסבר על שידורים"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"שידור"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"אנשים בקרבת מקום עם מכשירי Bluetooth תואמים יכולים להאזין למדיה שמשודרת על ידך"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"המצלמה והמיקרופון כבויים"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{התראה אחת}one{# התראות}two{# התראות}other{# התראות}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"כתיבת הערות"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"שידור"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"האם להפסיק לשדר את התוכן מאפליקציית <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"אם משדרים את התוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g> או משנים את הפלט, השידור הנוכחי יפסיק לפעול"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"שידור תוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"שינוי הפלט"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"לא ידוע"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"יום EEE, d בMMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"פתיחת <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 0cacdbdaa724..f15540cc28ef 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -993,7 +993,6 @@ <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> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> を開く"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 038937dd1027..0626dd62d931 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -993,7 +993,6 @@ <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> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"დდდ, თთთ თ"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"სთ:წთ"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"სთ:წთ"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> აპის გახსნა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 91c0b3596de5..1692bd4c392c 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Трансляцияны тоқтату"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Аудио шығыс үшін қолжетімді құрылғылар бар."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дыбыс деңгейі"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Тарату қалай жүзеге асады"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Тарату"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Үйлесімді Bluetooth құрылғылары бар маңайдағы адамдар сіз таратып жатқан медиамазмұнды тыңдай алады."</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера мен микрофон өшірулі"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# хабарландыру}other{# хабарландыру}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Ескертпе жазу"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Таратуда"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын таратуды тоқтатасыз ба?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын таратсаңыз немесе аудио шығысын өзгертсеңіз, қазіргі тарату сеансы тоқтайды."</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын тарату"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Аудио шығысын өзгерту"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Белгісіз"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM EEEE"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ашу"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 51e5b53c7f22..9158663983c7 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"បញ្ឈប់ការភ្ជាប់"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ឧបករណ៍ដែលអាចប្រើបានសម្រាប់ឧបករណ៍បញ្ចេញសំឡេង។"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"កម្រិតសំឡេង"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណើរការ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ការផ្សាយ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"មនុស្សនៅជិតអ្នកដែលមានឧបករណ៍ប៊្លូធូសត្រូវគ្នាអាចស្តាប់មេឌៀដែលអ្នកកំពុងផ្សាយបាន"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"កាមេរ៉ា និងមីក្រូហ្វូនត្រូវបានបិទ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{ការជូនដំណឹង #}other{ការជូនដំណឹង #}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"ការកត់ត្រា"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ការផ្សាយ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"បញ្ឈប់ការផ្សាយ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ប្រសិនបើអ្នកផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ឬប្ដូរឧបករណ៍បញ្ចេញសំឡេង ការផ្សាយបច្ចុប្បន្នរបស់អ្នកនឹងបញ្ឈប់"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ការផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ប្ដូរឧបករណ៍បញ្ចេញសំឡេង"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"មិនស្គាល់"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"បើក <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 5baf68fae084..abfa865390c3 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -993,7 +993,6 @@ <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> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 9b8e46a38df0..d1ca240bc60e 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"전송 중지"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"오디오 출력에 사용 가능한 기기입니다."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"볼륨"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"브로드캐스팅 작동 원리"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"브로드캐스트"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"호환되는 블루투스 기기를 가진 근처의 사용자가 내가 브로드캐스트 중인 미디어를 수신 대기할 수 있습니다."</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"카메라 및 마이크가 사용 중지되었습니다."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{알림 #개}other{알림 #개}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"메모"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"방송 중"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> 방송을 중지하시겠습니까?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 앱을 방송하거나 출력을 변경하면 기존 방송이 중단됩니다"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 방송"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"출력 변경"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"알 수 없음"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d일 EEE"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> 열기"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index bb55c627df73..93092ce0643d 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Тышкы экранга чыгарууну токтотуу"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Аудио чыгаруу үчүн жеткиликтүү түзмөктөр."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Үндүн катуулугу"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Кабарлоо"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Шайкеш Bluetooth түзмөктөрү болгон жакын жердеги кишилер кабарлап жаткан медиаңызды уга алышат"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера жана микрофон өчүк"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# билдирме}other{# билдирме}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Эскертме жазуу"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Кеңири таратуу"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда кабарлоо токтотулсунбу?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Эгер <xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарласаңыз же аудионун чыгуусун өзгөртсөңүз, учурдагы кабарлоо токтотулат"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарлоо"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Аудионун чыгуусун өзгөртүү"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Белгисиз"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ачуу"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index da353e644b65..906c72308cda 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ຢຸດການສົ່ງສັນຍານ"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ອຸປະກອນທີ່ສາມາດໃຊ້ໄດ້ສຳລັບເອົ້າພຸດສຽງ."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ລະດັບສຽງ"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ການອອກອາກາດເຮັດວຽກແນວໃດ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ອອກອາກາດ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ຄົນທີ່ຢູ່ໃກ້ທ່ານທີ່ມີອຸປະກອນ Bluetooth ທີ່ເຂົ້າກັນໄດ້ຈະສາມາດຟັງມີເດຍທີ່ທ່ານກຳລັງອອກອາກາດຢູ່ໄດ້"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ປິດກ້ອງຖ່າຍຮູບ ແລະ ໄມແລ້ວ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ການແຈ້ງເຕືອນ}other{# ການແຈ້ງເຕືອນ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"ການຈົດບັນທຶກ"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ກຳລັງອອກອາກາດ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ຢຸດການອອກອາກາດ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ຫາກທ່ານອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ຫຼື ປ່ຽນເອົ້າພຸດ, ການອອກອາກາດປັດຈຸບັນຂອງທ່ານຈະຢຸດ"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ປ່ຽນເອົ້າພຸດ"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ບໍ່ຮູ້ຈັກ"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ຊມ:ນທ"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ຊມ:ນທ"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"ເປີດ <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 96c43712a2f6..789b2b94345b 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sustabdyti perdavimą"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Pasiekiami garso išvesties įrenginiai."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Garsumas"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transliacija"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Netoliese esantys žmonės, turintys suderinamus „Bluetooth“ įrenginius, gali klausyti jūsų transliuojamos medijos"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Vaizdo kamera ir mikrofonas išjungti"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pranešimas}one{# pranešimas}few{# pranešimai}many{# pranešimo}other{# pranešimų}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Užrašų kūrimas"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transliavimas"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Sustabdyti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ transliaciją?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jei transliuosite „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“ arba pakeisite išvestį, dabartinė transliacija bus sustabdyta"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transliuoti „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Keisti išvestį"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nežinoma"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Atidaryti „<xliff:g id="APPNAME">%1$s</xliff:g>“"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 1c2238014b55..dbdbf71b2ce4 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Apturēt apraidi"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Audio izvadei pieejamās ierīces."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Skaļums"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Apraide"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Tuvumā esošās personas ar saderīgām Bluetooth ierīcēm var klausīties jūsu apraidīto multivides saturu."</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera un mikrofons ir izslēgti"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# paziņojums}zero{# paziņojumu}one{# paziņojums}other{# paziņojumi}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Piezīmju pierakstīšana"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Notiek apraidīšana"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vai apturēt lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> apraidīšanu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ja sāksiet lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraidīšanu vai mainīsiet izvadi, pašreizējā apraide tiks apturēta"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraide"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Izvades maiņa"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nezināms"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Atvērt lietotni <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 603151fb1aea..2d35cdb7609e 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -993,7 +993,6 @@ <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> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отворете ја <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 5df73055cef2..409b578ef2e6 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"കാസ്റ്റ് ചെയ്യുന്നത് നിർത്തുക"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ഓഡിയോ ഔട്ട്പുട്ടിന് ലഭ്യമായ ഉപകരണങ്ങൾ."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"വോളിയം"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ബ്രോഡ്കാസ്റ്റ് എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത്"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ബ്രോഡ്കാസ്റ്റ്"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"അനുയോജ്യമായ Bluetooth ഉപകരണങ്ങളോടെ സമീപമുള്ള ആളുകൾക്ക് നിങ്ങൾ ബ്രോഡ്കാസ്റ്റ് ചെയ്യുന്ന മീഡിയ കേൾക്കാനാകും"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ക്യാമറയും മൈക്കും ഓഫാണ്"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# അറിയിപ്പ്}other{# അറിയിപ്പുകൾ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"പ്രക്ഷേപണം ചെയ്യുന്നു"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"നിങ്ങൾ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുകയോ ഔട്ട്പുട്ട് മാറ്റുകയോ ചെയ്താൽ നിങ്ങളുടെ നിലവിലുള്ള ബ്രോഡ്കാസ്റ്റ് അവസാനിക്കും"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുക"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ഔട്ട്പുട്ട് മാറ്റുക"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"അജ്ഞാതം"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> തുറക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index aa57e815b676..8c3a70ff057e 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Дамжуулахыг зогсоох"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Аудио гаралт хийх боломжтой төхөөрөмжүүд."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дууны түвшин"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Нэвтрүүлэлт хэрхэн ажилладаг вэ?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Нэвтрүүлэлт"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Тохиромжтой Bluetooth төхөөрөмжүүдтэй таны ойролцоох хүмүүс таны нэвтрүүлж буй медиаг сонсох боломжтой"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камер болон микрофон унтраалттай байна"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# мэдэгдэл}other{# мэдэгдэл}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Тэмдэглэл хөтлөх"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Нэвтрүүлэлт"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г нэвтрүүлэхээ зогсоох уу?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Хэрэв та <xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлсэн эсвэл гаралтыг өөрчилсөн бол таны одоогийн нэвтрүүлэлтийг зогсооно"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлэх"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Гаралтыг өөрчлөх"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Тодорхойгүй"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>-г нээх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 7f3c28cbbf7f..658305710d6a 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करणे थांबवा"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ऑडिओ आउटपुटसाठी उपलब्ध डिव्हाइस."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"व्हॉल्यूम"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्टिंग कसे काम करते"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करा"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कंपॅटिबल ब्लूटूथ डिव्हाइस असलेले तुमच्या जवळपासचे लोक हे तुम्ही ब्रॉडकास्ट करत असलेला मीडिया ऐकू शकतात"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"कॅमेरा आणि माइक बंद आहेत"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}other{# सूचना}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"नोटटेकिंग"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट करत आहे"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> चे प्रसारण थांबवायचे आहे का?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तुम्ही <xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण केल्यास किंवा आउटपुट बदलल्यास, तुमचे सध्याचे प्रसारण बंद होईल"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण करा"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपूट बदला"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"अज्ञात"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> उघडा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index bfbc8ea8a203..014011247ee8 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Tukar output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tidak diketahui"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buka <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 909b9ab031a7..164d662f969b 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -874,8 +874,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ကာစ် ရပ်ရန်"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"အသံအထွက်အတွက် ရရှိနိုင်သောစက်များ။"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"အသံအတိုးအကျယ်"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတ်လွှင့်မှုဆောင်ရွက်ပုံ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ထုတ်လွှင့်ခြင်း"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"အနီးရှိတွဲသုံးနိုင်သော ဘလူးတုသ်သုံးစက် အသုံးပြုသူများက သင်ထုတ်လွှင့်နေသော မီဒီယာကို နားဆင်နိုင်သည်"</string> @@ -989,15 +988,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ကင်မရာနှင့် မိုက် ပိတ်ထားသည်"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{အကြောင်းကြားချက် # ခု}other{အကြောင်းကြားချက် # ခု}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>၊ <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"မှတ်စုလိုက်ခြင်း"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ထုတ်လွှင့်ခြင်း"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ထုတ်လွှင့်ခြင်းကို ရပ်မလား။"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ကို ထုတ်လွှင့်သောအခါ (သို့) အထွက်ကို ပြောင်းသောအခါ သင့်လက်ရှိထုတ်လွှင့်ခြင်း ရပ်သွားမည်"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ထုတ်လွှင့်ခြင်း"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"အထွက်ကို ပြောင်းခြင်း"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"မသိ"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ဖွင့်ရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 95846f05a008..6288a15b1d4e 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stopp castingen"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Tilgjengelige enheter for lydutgang."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Kringkasting"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Folk i nærheten med kompatible Bluetooth-enheter kan lytte til mediene du kringkaster"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera og mikrofon er av"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# varsel}other{# varsler}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Notatskriving"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Kringkaster"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vil du stoppe kringkastingen av <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du kringkaster <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller endrer utgangen, stopper den nåværende kringkastingen din"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Kringkast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Endre utgang"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ukjent"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Åpne <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index b04e368b2905..563d6082cf9d 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -500,8 +500,7 @@ <string name="wallet_error_generic" msgid="257704570182963611">"तपाईंका कार्डहरू प्राप्त गर्ने क्रममा समस्या भयो, कृपया पछि फेरि प्रयास गर्नुहोस्"</string> <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लक स्क्रिनसम्बन्धी सेटिङ"</string> <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR कोड स्क्यानर"</string> - <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) --> - <skip /> + <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट गरिँदै छ"</string> <string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string> <string name="zen_alarm_warning" msgid="7844303238486849503">"तपाईँले आफ्नो अर्को अलार्म <xliff:g id="WHEN">%1$s</xliff:g> सुन्नुहुने छैन"</string> @@ -873,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट गर्न छाड्नुहोस्"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"अडियो आउटपुटका लागि उपलब्ध डिभाइसहरू।"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"भोल्युम"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"प्रसारण गर्ने सुविधाले कसरी काम गर्छ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"प्रसारण"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कम्प्याटिबल ब्लुटुथ डिभाइस भएका नजिकैका मान्छेहरू तपाईंले प्रसारण गरिरहनुभएको मिडिया सुन्न सक्छन्"</string> @@ -988,31 +986,22 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"क्यामेरा र माइक अफ छन्"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# वटा सूचना}other{# वटा सूचनाहरू}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"टिपोट गर्ने कार्य"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"प्रसारण गरिँदै छ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ब्रोडकास्ट गर्न छाड्ने हो?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तपाईंले <xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुभयो वा आउटपुट परिवर्तन गर्नुभयो भने तपाईंको हालको ब्रोडकास्ट रोकिने छ"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुहोस्"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपुट परिवर्तन गर्नुहोस्"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"अज्ञात"</string> - <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> - <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) --> - <skip /> - <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) --> - <skip /> - <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) --> - <skip /> - <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) --> - <skip /> - <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) --> - <skip /> - <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) --> - <skip /> - <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) --> - <skip /> + <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> खोल्नुहोस्"</string> + <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> एपलाई सर्टकटका रूपमा हाल्न, निम्न कुराको सुनिश्चित गर्नुहोस्:"</string> + <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• एप सेटअप गरिएको छ"</string> + <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Wallet मा कम्तीमा एउटा कार्ड हालिएको छ"</string> + <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• क्यामेरा एप इन्स्टल गरिएको छ"</string> + <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• एप सेटअप गरिएको छ"</string> + <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• कम्तीमा एउटा डिभाइस उपलब्ध छ"</string> <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"रद्द गर्नुहोस्"</string> <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"अहिले नै फ्लिप गर्नुहोस्"</string> <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"अझ राम्रो सेल्फी खिच्न फोन अनफोल्ड गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 0c05b12a8e6d..ee73a6fcc9cb 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Casten stoppen"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Beschikbare apparaten voor audio-uitvoer."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Uitzending"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mensen bij jou in de buurt met geschikte bluetooth-apparaten kunnen luisteren naar de media die je uitzendt"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera en microfoon staan uit"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# melding}other{# meldingen}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Aantekeningen maken"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitzending"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Uitzending van <xliff:g id="APP_NAME">%1$s</xliff:g> stopzetten?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Als je <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzendt of de uitvoer wijzigt, wordt je huidige uitzending gestopt"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzenden"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Uitvoer wijzigen"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Onbekend"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d mmm"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"u:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> openen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 307b7b9841e3..455f1f9f8374 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରନ୍ତୁ"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ଅଡିଓ ଆଉଟପୁଟ ପାଇଁ ଉପଲବ୍ଧ ଡିଭାଇସଗୁଡ଼ିକ।"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ଭଲ୍ୟୁମ"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ବ୍ରଡକାଷ୍ଟିଂ କିପରି କାମ କରେ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ବ୍ରଡକାଷ୍ଟ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ଆପଣଙ୍କ ଆଖପାଖର କମ୍ପାଟିବଲ ବ୍ଲୁଟୁଥ ଡିଭାଇସ ଥିବା ଲୋକମାନେ ଆପଣ ବ୍ରଡକାଷ୍ଟ କରୁଥିବା ମିଡିଆ ଶୁଣିପାରିବେ"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"କ୍ୟାମେରା ଏବଂ ମାଇକ ବନ୍ଦ ଅଛି"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#ଟି ବିଜ୍ଞପ୍ତି}other{#ଟି ବିଜ୍ଞପ୍ତି}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"ନୋଟଟେକିଂ"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ବ୍ରଡକାଷ୍ଟ କରୁଛି"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ଯଦି ଆପଣ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତି କିମ୍ବା ଆଉଟପୁଟ ବଦଳାନ୍ତି, ତେବେ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ବ୍ରଡକାଷ୍ଟ ବନ୍ଦ ହୋଇଯିବ"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତୁ"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ଆଉଟପୁଟ ବଦଳାନ୍ତୁ"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ଅଜଣା"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ଖୋଲନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index ccf27d731ec0..3e3d3e86a0fd 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ਆਡੀਓ ਆਊਟਪੁੱਟ ਲਈ ਉਪਲਬਧ ਡੀਵਾਈਸ।"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ਅਵਾਜ਼"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ਪ੍ਰਸਾਰਨ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ਪ੍ਰਸਾਰਨ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ਅਨੁਰੂਪ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨਾਲ ਨਜ਼ਦੀਕੀ ਲੋਕ ਤੁਹਾਡੇ ਵੱਲੋਂ ਪ੍ਰਸਾਰਨ ਕੀਤੇ ਜਾ ਰਹੇ ਮੀਡੀਆ ਨੂੰ ਸੁਣ ਸਕਦੇ ਹਨ"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ ਬੰਦ ਹਨ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ਸੂਚਨਾ}one{# ਸੂਚਨਾ}other{# ਸੂਚਨਾਵਾਂ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"ਨੋਟ ਬਣਾਉਣਾ"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ਪ੍ਰਸਾਰਨ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਪ੍ਰਸਾਰਨ ਨੂੰ ਰੋਕਣਾ ਹੈ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ਜੇ ਤੁਸੀਂ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਦੇ ਹੋ ਜਾਂ ਆਊਟਪੁੱਟ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਪ੍ਰਸਾਰਨ ਰੁਕ ਜਾਵੇਗਾ"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰੋ"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ਆਊਟਪੁੱਟ ਬਦਲੋ"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ਅਗਿਆਤ"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ਖੋਲ੍ਹੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index c82f75cd30d4..1b68eecdcae0 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zatrzymaj przesyłanie"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dostępne urządzenia do odtwarzania dźwięku."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Głośność"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak działa transmitowanie"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisja"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osoby w pobliżu ze zgodnymi urządzeniami Bluetooth mogą słuchać transmitowanych przez Ciebie multimediów"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Aparat i mikrofon są wyłączone"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# powiadomienie}few{# powiadomienia}many{# powiadomień}other{# powiadomienia}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Notatki"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmisja"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zatrzymaj transmisję aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jeśli transmitujesz aplikację <xliff:g id="SWITCHAPP">%1$s</xliff:g> lub zmieniasz dane wyjściowe, Twoja obecna transmisja zostanie zakończona"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmisja aplikacji <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Zmień dane wyjściowe"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Brak informacji"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otwórz: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index f6a1c5c3ac58..0e7c68a342ff 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Mudar saída"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecido"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Usar o display frontal para tirar uma selfie melhor?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use a câmera traseira para tirar uma foto mais ampla e com maior resolução."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 98b5c7695383..a38550f5ce25 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponíveis para a saída de áudio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmissão"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas de si com dispositivos Bluetooth compatíveis podem ouvir o conteúdo multimédia que está a transmitir"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmara e o microfone estão desativados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}many{# notificações}other{# notificações}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Tomar notas"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"A transmitir"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão da app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se transmitir a app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou alterar a saída, a sua transmissão atual é interrompida"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmita a app <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Altere a saída"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecida"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index f6a1c5c3ac58..0e7c68a342ff 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Mudar saída"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecido"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1009,8 +1008,6 @@ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Usar o display frontal para tirar uma selfie melhor?"</string> <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use a câmera traseira para tirar uma foto mais ampla e com maior resolução."</string> <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string> - <!-- no translation found for rear_display_accessibility_folded_animation (1538121649587978179) --> - <skip /> - <!-- no translation found for rear_display_accessibility_unfolded_animation (1946153682258289040) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index e9257fb8c662..ba5eea39e7ae 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmite <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbă rezultatul"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Necunoscută"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE, z LLL"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Deschide <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 5128e0e0aec6..5a3f098d81e0 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -993,7 +993,6 @@ <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> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM EEEE"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Открыть \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index f1767957a5be..d1fa46ef0348 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"විකාශය නවතන්න"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ශ්රව්ය ප්රතිදානය සඳහා තිබෙන උපාංග."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"හඬ පරිමාව"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනය ක්රියා කරන ආකාරය"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"විකාශනය"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ගැළපෙන බ්ලූටූත් උපාංග සහිත ඔබ අවට සිටින පුද්ගලයින්ට ඔබ විකාශනය කරන මාධ්යයට සවන් දිය හැකිය"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"කැමරාව සහ මයික් ක්රියාවිරහිතයි"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{දැනුම්දීම් #ක්}one{දැනුම්දීම් #ක්}other{දැනුම්දීම් #ක්}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"සටහන් කර ගැනීම"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"විකාශනය කරමින්"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> විකාශනය කිරීම නවත්වන්නද?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ඔබ <xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය කළහොත් හෝ ප්රතිදානය වෙනස් කළහොත්, ඔබගේ වත්මන් විකාශනය නවතිනු ඇත."</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ප්රතිදානය වෙනස් කරන්න"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"නොදනී"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> විවෘත කරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index e4c978b1f786..2573733fc50b 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Vysielanie aplikácie <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Zmena výstupu"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznáme"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvoriť <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index ef5e63ade170..fa63f87650c6 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ustavi predvajanje"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Razpoložljive naprave za zvočni izhod"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Glasnost"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Oddajanje"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osebe v bližini z združljivo napravo Bluetooth lahko poslušajo predstavnost, ki jo oddajate."</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparat in mikrofon sta izklopljena."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obvestilo}one{# obvestilo}two{# obvestili}few{# obvestila}other{# obvestil}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Ustvarjanje zapiskov"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Oddajanje"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite ustaviti oddajanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Če oddajate aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g> ali spremenite izhod, bo trenutno oddajanje ustavljeno."</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Oddajaj aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Sprememba izhoda"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznano"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Odpri aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 24e26168a9ec..6eb745ccdfaa 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmeto <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ndrysho daljen"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"I panjohur"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Hap \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 9464ac51a7f4..75a455997f84 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -993,7 +993,6 @@ <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> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"ДДД, д. МММ"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"с:мин"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ч:мин"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отворите: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index f5cf437fb1eb..39b40e97b3b3 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sluta casta"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Enheter som är tillgängliga för ljudutdata."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volym"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Utsändning"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i närheten med kompatibla Bluetooth-enheter kan lyssna på medieinnehåll som du sänder ut"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kameran och mikrofonen är avstängda"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# avisering}other{# aviseringar}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Anteckningar"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sänder"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vill du sluta sända från <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Om en utsändning från <xliff:g id="SWITCHAPP">%1$s</xliff:g> pågår eller om du byter ljudutgång avbryts den nuvarande utsändningen"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Sänd från <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Byt ljudutgång"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Okänt"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Öppna <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index e0043e39b99d..ccf3345e2820 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -995,7 +995,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Tangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Badilisha maudhui"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Haijulikani"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"saa:dk"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:dk"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Fungua <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index f800cd0ebab9..9f99b76ea984 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"அலைபரப்புவதை நிறுத்து"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ஆடியோ அவுட்புட்டுக்குக் கிடைக்கும் சாதனங்கள்."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ஒலியளவு"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"பிராட்காஸ்ட் எவ்வாறு செயல்படுகிறது?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"பிராட்காஸ்ட்"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"நீங்கள் பிராட்காஸ்ட் செய்யும் மீடியாவை அருகிலுள்ளவர்கள் இணக்கமான புளூடூத் சாதனங்கள் மூலம் கேட்கலாம்"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"கேமராவும் மைக்கும் ஆஃப் செய்யப்பட்டுள்ளன"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# அறிவிப்பு}other{# அறிவிப்புகள்}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"குறிப்பெடுத்தல்"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ஒலிபரப்புதல்"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒலிபரப்பப்படுவதை நிறுத்தவா?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"நீங்கள் <xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பினாலோ அவுட்புட்டை மாற்றினாலோ உங்களின் தற்போதைய ஒலிபரப்பு நிறுத்தப்படும்"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பு"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"அவுட்புட்டை மாற்று"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"தெரியவில்லை"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸைத் திற"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index f707088ccef5..4e506d4eadab 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -66,7 +66,7 @@ <string name="usb_contaminant_title" msgid="894052515034594113">"USB పోర్ట్ నిలిపివేయబడింది"</string> <string name="usb_contaminant_message" msgid="7730476585174719805">"మీ పరికరంలోకి నీరు లేదా చెత్తాచెదారం చేరిపోకుండా కాపాడటానికి, USB పోర్ట్ నిలిపివేయబడుతుంది, అలాగే యాక్సెసరీలు వేటిని గుర్తించదు.\n\nUSB పోర్ట్ను ఉపయోగించడం సురక్షితమేనని నిర్ధారించుకున్న తర్వాత, మళ్లీ మీకో నోటిఫికేషన్ రూపంలో తెలియజేయబడుతుంది."</string> <string name="usb_port_enabled" msgid="531823867664717018">"ఛార్జర్లు, యాక్సెసరీలను గుర్తించే విధంగా USB పోర్ట్ ప్రారంభించబడింది"</string> - <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"USBని ప్రారంభించు"</string> + <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"USBని ప్రారంభించండి"</string> <string name="learn_more" msgid="4690632085667273811">"మరింత తెలుసుకోండి"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్షాట్"</string> <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock డిజేబుల్ చేయబడింది"</string> @@ -105,7 +105,7 @@ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే మ్యూజిక్, కాల్స్, రింగ్టోన్ల వంటి ధ్వనులు"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"మైక్రోఫోన్"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"పరికరం ఆడియో, మైక్రోఫోన్"</string> - <string name="screenrecord_start" msgid="330991441575775004">"ప్రారంభించు"</string> + <string name="screenrecord_start" msgid="330991441575775004">"ప్రారంభించండి"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"స్క్రీన్ రికార్డింగ్ చేయబడుతోంది"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"స్క్రీన్, ఆడియో రికార్డింగ్ చేయబడుతున్నాయి"</string> <string name="screenrecord_taps_label" msgid="1595690528298857649">"స్క్రీన్పై తాకే స్థానాలను చూపు"</string> @@ -289,7 +289,7 @@ <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ప్రారంభించబడింది"</string> <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"స్క్రీన్ రికార్డ్"</string> - <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించు"</string> + <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించండి"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆపు"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయమంటారా?"</string> @@ -356,7 +356,7 @@ <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"గెస్ట్కు తిరిగి స్వాగతం!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> - <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> + <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించండి"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"అవును, కొనసాగించు"</string> <string name="guest_notification_app_name" msgid="2110425506754205509">"గెస్ట్ మోడ్"</string> <string name="guest_notification_session_active" msgid="5567273684713471450">"మీరు గెస్ట్ మోడ్లో ఉన్నారు"</string> @@ -391,7 +391,7 @@ <string name="notification_section_header_conversations" msgid="821834744538345661">"సంభాషణలు"</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="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string> <string name="empty_shade_text" msgid="8935967157319717412">"నోటిఫికేషన్లు లేవు"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"కొత్త నోటిఫికేషన్లు ఏవీ లేవు"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"పాత నోటిఫికేషన్ల కోసం అన్లాక్ చేయండి"</string> @@ -442,7 +442,7 @@ <string name="volume_odi_captions_tip" msgid="8825655463280990941">"మీడియాకు ఆటోమేటిక్ క్యాప్షన్లు"</string> <string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"క్యాప్షన్ల చిట్కాను మూసివేయండి"</string> <string name="volume_odi_captions_content_description" msgid="4172765742046013630">"క్యాప్షన్లు ఓవర్లే"</string> - <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"ప్రారంభించు"</string> + <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"ప్రారంభించండి"</string> <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"నిలిపివేయండి"</string> <string name="sound_settings" msgid="8874581353127418308">"సౌండ్ & వైబ్రేషన్"</string> <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"సెట్టింగ్లు"</string> @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ప్రసారాన్ని ఆపివేయండి"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ఆడియో అవుట్పుట్ కోసం అందుబాటులో ఉన్న పరికరాలు."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"వాల్యూమ్"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ప్రసారం కావడం అనేది ఎలా పని చేస్తుంది"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ప్రసారం"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"మీకు సమీపంలో ఉన్న వ్యక్తులు అనుకూలత ఉన్న బ్లూటూత్ పరికరాలతో మీరు ప్రసారం చేస్తున్న మీడియాను వినగలరు"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"కెమెరా, మైక్ ఆఫ్లో ఉన్నాయి"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# నోటిఫికేషన్}other{# నోటిఫికేషన్లు}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"నోట్టేకింగ్"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ప్రసారం చేస్తోంది"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రసారం చేయడాన్ని ఆపివేయాలా?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"మీరు <xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేస్తే లేదా అవుట్పుట్ను మార్చినట్లయితే, మీ ప్రస్తుత ప్రసారం ఆగిపోతుంది"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేయండి"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"అవుట్పుట్ను మార్చండి"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"తెలియదు"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>ను తెరవండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 40b997d3e55b..544dde40d5f3 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"หยุดแคสต์"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"อุปกรณ์ที่พร้อมใช้งานสำหรับเอาต์พุตเสียง"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ระดับเสียง"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"วิธีการทำงานของการออกอากาศ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ประกาศ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ผู้ที่อยู่ใกล้คุณและมีอุปกรณ์บลูทูธที่รองรับสามารถรับฟังสื่อที่คุณกำลังออกอากาศได้"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"กล้องและไมค์ปิดอยู่"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{การแจ้งเตือน # รายการ}other{การแจ้งเตือน # รายการ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"การจดบันทึก"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"กำลังออกอากาศ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"หยุดการออกอากาศ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"หากคุณออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g> หรือเปลี่ยนแปลงเอาต์พุต การออกอากาศในปัจจุบันจะหยุดลง"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"เปลี่ยนเอาต์พุต"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ไม่ทราบ"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d MMM"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"HH:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"เปิด <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index a0b4e29a7e5e..c27b13308bf8 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ihinto ang pag-cast"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Mga available na device para sa audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Makakapakinig ang mga taong malapit sa iyo na may mga compatible na Bluetooth device sa media na bino-broadcast mo"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Naka-off ang camera at mikropono"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# na notification}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Pagtatala"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Nagbo-broadcast"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ihinto ang pag-broadcast ng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kung magbo-broadcast ka ng <xliff:g id="SWITCHAPP">%1$s</xliff:g> o babaguhin mo ang output, hihinto ang iyong kasalukuyang broadcast"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"I-broadcast ang <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Baguhin ang output"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Hindi alam"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buksan ang <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 4fb45b182daf..90cd4a7e6f73 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayını durdur"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Ses çıkışı için kullanılabilir cihazlar."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ses düzeyi"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın işleyiş şekli"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Anons"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Yakınınızda ve uyumlu Bluetooth cihazları olan kişiler yayınladığınız medya içeriğini dinleyebilir"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera ve mikrofon kapalı"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildirim}other{# bildirim}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Not alma"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayınlama"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında anons durdurulsun mu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapar veya çıkışı değiştirirseniz mevcut anonsunuz duraklatılır"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapın"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Çıkışı değiştirme"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Bilinmiyor"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM, EEE"</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:dd"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulamasını aç"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 5992b015a951..76e99ef4bc3b 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Припинити трансляцію"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Доступні пристрої для відтворення звуку."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучність"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як працює трансляція"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляція"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Люди поблизу, які мають сумісні пристрої з Bluetooth, можуть слухати медіаконтент, який ви транслюєте."</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камеру й мікрофон вимкнено"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# сповіщення}one{# сповіщення}few{# сповіщення}many{# сповіщень}other{# сповіщення}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Створення нотаток"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляція"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Зупинити трансляцію з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Якщо ви зміните додаток (<xliff:g id="SWITCHAPP">%1$s</xliff:g>) або аудіовихід, поточну трансляцію буде припинено"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Змінити додаток для трансляції на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Змінити аудіовихід"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Невідомо"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Відкрити <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 1a0ce6f21126..7f805984dc1b 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -993,7 +993,6 @@ <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> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> کھولیں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 257edd4b46cb..99e8abc13b6b 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -993,7 +993,6 @@ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ovoz chiqishini oʻzgartirish"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Noaniq"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:dd"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ochish: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 53efd2d07987..88bf8de25e09 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dừng truyền"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Các thiết bị có sẵn để xuất âm thanh."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Âm lượng"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoạt động"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Truyền"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Những người ở gần có thiết bị Bluetooth tương thích có thể nghe nội dung nghe nhìn bạn đang truyền"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Máy ảnh và micrô đang tắt"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# thông báo}other{# thông báo}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Ghi chú"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Phát sóng"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Dừng phát <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nếu bạn phát <xliff:g id="SWITCHAPP">%1$s</xliff:g> hoặc thay đổi đầu ra, phiên truyền phát hiện tại sẽ dừng"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Phát <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Thay đổi đầu ra"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Không xác định"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Mở <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index c339e8dc9a5a..81b61c6644e7 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"音频输出的可用设备。"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"广播的运作方式"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"广播"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近使用兼容蓝牙设备的用户可以收听您广播的媒体内容"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"摄像头和麦克风已关闭"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 条通知}other{# 条通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"记录"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"正在广播"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"更改输出来源"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"未知"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"打开<xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 6ed283538a5c..3d7ba058a0eb 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"可用作音訊輸出的裝置"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播運作方式"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近有兼容藍牙裝置的人可收聽您正在廣播的媒體內容"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"相機和麥克風已關閉"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"做筆記"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止廣播「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如要廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止廣播目前的內容"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"變更輸出來源"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"開啟「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index b1698d52e16d..47c0561c39a0 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"可用於輸出音訊的裝置。"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播功能的運作方式"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"如果附近的人有相容的藍牙裝置,就可以聽到你正在廣播的媒體內容"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"已關閉相機和麥克風"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"做筆記"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止播送「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止播送目前的內容"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"變更輸出來源"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string> - <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"開啟「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 38a61365932f..b77851954f50 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -872,8 +872,7 @@ <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Misa ukusakaza"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Amadivayisi atholakalayo okukhipha umsindo."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ivolumu"</string> - <!-- no translation found for media_output_dialog_volume_percentage (1613984910585111798) --> - <skip /> + <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Sakaza"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Abantu abaseduze nawe abanamadivayisi e-Bluetooth ahambisanayo bangalalela imidiya oyisakazayo"</string> @@ -987,15 +986,13 @@ <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Ikhamera nemakrofoni kuvaliwe"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Isaziso esingu-#}one{Izaziso ezingu-#}other{Izaziso ezingu-#}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <!-- no translation found for note_task_button_label (8718616095800343136) --> - <skip /> + <string name="note_task_button_label" msgid="8718616095800343136">"Ukuthatha amanothi"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Ukusakaza"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Misa ukusakaza i-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Uma usakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g> noma ushintsha okuphumayo, ukusakaza kwakho kwamanje kuzoma"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Sakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Shintsha okuphumayo"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Akwaziwa"</string> - <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> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Vula i-<xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 6d88e512eddb..2d756ae25b3c 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -779,6 +779,10 @@ <!-- Duration in milliseconds of the y-translation animation when entering a dream --> <integer name="config_dreamOverlayInTranslationYDurationMs">917</integer> + <!-- Delay in milliseconds before switching to the dock user and dreaming if a secondary user is + active when the device is locked and docked. 0 indicates disabled. Default is 1 minute. --> + <integer name="config_defaultDockUserTimeoutMs">60000</integer> + <!-- Icons that don't show in a collapsed non-keyguard statusbar --> <string-array name="config_collapsed_statusbar_icon_blocklist" translatable="false"> <item>@*android:string/status_bar_volume</item> @@ -820,6 +824,8 @@ slot. If the user did make a choice, even if the choice is the "None" option, the default is ignored. --> <string-array name="config_keyguardQuickAffordanceDefaults" translatable="false"> + <item>bottom_start:home</item> + <item>bottom_end:wallet</item> </string-array> </resources> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index fd2e3249d59a..c5ffc94d01af 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -30,6 +30,9 @@ <bool name="flag_charging_ripple">false</bool> + <!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. --> + <bool name="flag_active_unlock_chipbar">true</bool> + <bool name="flag_smartspace">false</bool> <!-- Whether the user switcher chip shows in the status bar. When true, the multi user diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 7ca42f7d7015..b5ac9405be0c 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -142,6 +142,9 @@ <item type="id" name="row_tag_for_content_view" /> + <!-- Chipbar --> + <item type="id" name="tag_chipbar_info"/> + <!-- Optional cancel button on Keyguard --> <item type="id" name="cancel_button"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9d5a8c4d3cc7..ca2cf1a56b41 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2762,4 +2762,7 @@ <string name="rear_display_accessibility_folded_animation">Foldable device being unfolded</string> <!-- Text for education page content description for unfolded animation. [CHAR_LIMIT=NONE] --> <string name="rear_display_accessibility_unfolded_animation">Foldable device being flipped around</string> + + <!-- Title for notification of low stylus battery. [CHAR_LIMIT=NONE] --> + <string name="stylus_battery_low">Stylus battery low</string> </resources> diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml index 64c2ef1fc915..7de0a5e0e8c4 100644 --- a/packages/SystemUI/res/xml/media_session_expanded.xml +++ b/packages/SystemUI/res/xml/media_session_expanded.xml @@ -88,20 +88,18 @@ The chain is set to "spread" so that the progress bar can be weighted to fill any empty space. --> <Constraint - android:id="@+id/media_scrubbing_elapsed_time" + android:id="@+id/actionPrev" android:layout_width="48dp" android:layout_height="48dp" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toLeftOf="@id/actionPrev" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_chainStyle="spread" /> <Constraint - android:id="@+id/actionPrev" + android:id="@+id/media_scrubbing_elapsed_time" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/media_scrubbing_elapsed_time" - app:layout_constraintRight_toLeftOf="@id/media_progress_bar" + app:layout_constraintLeft_toRightOf="@id/actionPrev" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_chainStyle="spread" /> @@ -109,7 +107,7 @@ android:id="@+id/media_progress_bar" android:layout_width="0dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/actionPrev" + app:layout_constraintLeft_toRightOf="@id/media_scrubbing_elapsed_time" app:layout_constraintRight_toLeftOf="@id/actionNext" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_weight="1" /> @@ -118,7 +116,6 @@ android:id="@+id/actionNext" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/media_progress_bar" app:layout_constraintRight_toLeftOf="@id/media_scrubbing_total_time" app:layout_constraintBottom_toBottomOf="parent" /> @@ -126,7 +123,6 @@ android:id="@+id/media_scrubbing_total_time" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/actionNext" app:layout_constraintRight_toLeftOf="@id/action0" app:layout_constraintBottom_toBottomOf="parent" /> @@ -134,7 +130,6 @@ android:id="@+id/action0" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/media_scrubbing_total_time" app:layout_constraintRight_toLeftOf="@id/action1" app:layout_constraintBottom_toBottomOf="parent" /> @@ -142,7 +137,6 @@ android:id="@+id/action1" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/action0" app:layout_constraintRight_toLeftOf="@id/action2" app:layout_constraintBottom_toBottomOf="parent" /> @@ -150,7 +144,6 @@ android:id="@+id/action2" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/action1" app:layout_constraintRight_toLeftOf="@id/action3" app:layout_constraintBottom_toBottomOf="parent" /> @@ -158,7 +151,6 @@ android:id="@+id/action3" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/action2" app:layout_constraintRight_toLeftOf="@id/action4" app:layout_constraintBottom_toBottomOf="parent" /> @@ -166,7 +158,6 @@ android:id="@+id/action4" android:layout_width="48dp" android:layout_height="48dp" - app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> </ConstraintSet> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java index 6087655615a6..8ee893c14727 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java @@ -71,7 +71,7 @@ import java.util.function.Supplier; */ public class RotationButtonController { - private static final String TAG = "StatusBar/RotationButtonController"; + private static final String TAG = "RotationButtonController"; private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100; private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000; private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); @@ -377,6 +377,7 @@ public class RotationButtonController { } // Prepare to show the navbar icon by updating the icon style to change anim params + Log.i(TAG, "onRotationProposal(rotation=" + rotation + ")"); mLastRotationSuggestion = rotation; // Remember rotation for click final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation); if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) { @@ -499,6 +500,7 @@ public class RotationButtonController { mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED); incrementNumAcceptedRotationSuggestionsIfNeeded(); setRotationLockedAtAngle(mLastRotationSuggestion); + Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion); v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt new file mode 100644 index 000000000000..3a89c13ddd64 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt @@ -0,0 +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.keyguard + +import android.annotation.CurrentTimeMillisLong +import com.android.systemui.dump.DumpsysTableLogger +import com.android.systemui.dump.Row +import com.android.systemui.plugins.util.RingBuffer + +/** Verbose debug information. */ +data class KeyguardActiveUnlockModel( + @CurrentTimeMillisLong override var timeMillis: Long = 0L, + override var userId: Int = 0, + override var listening: Boolean = false, + // keep sorted + var awakeKeyguard: Boolean = false, + var authInterruptActive: Boolean = false, + var fpLockedOut: Boolean = false, + var primaryAuthRequired: Boolean = false, + var switchingUser: Boolean = false, + var triggerActiveUnlockForAssistant: Boolean = false, + var userCanDismissLockScreen: Boolean = false, +) : KeyguardListenModel() { + + /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */ + val asStringList: List<String> by lazy { + listOf( + DATE_FORMAT.format(timeMillis), + timeMillis.toString(), + userId.toString(), + listening.toString(), + // keep sorted + awakeKeyguard.toString(), + authInterruptActive.toString(), + fpLockedOut.toString(), + primaryAuthRequired.toString(), + switchingUser.toString(), + triggerActiveUnlockForAssistant.toString(), + userCanDismissLockScreen.toString(), + ) + } + + /** + * [RingBuffer] to store [KeyguardActiveUnlockModel]. After the buffer is full, it will recycle + * old events. + * + * Do not use [append] to add new elements. Instead use [insert], as it will recycle if + * necessary. + */ + class Buffer { + private val buffer = RingBuffer(CAPACITY) { KeyguardActiveUnlockModel() } + + fun insert(model: KeyguardActiveUnlockModel) { + buffer.advance().apply { + timeMillis = model.timeMillis + userId = model.userId + listening = model.listening + // keep sorted + awakeKeyguard = model.awakeKeyguard + authInterruptActive = model.authInterruptActive + fpLockedOut = model.fpLockedOut + primaryAuthRequired = model.primaryAuthRequired + switchingUser = model.switchingUser + triggerActiveUnlockForAssistant = model.triggerActiveUnlockForAssistant + userCanDismissLockScreen = model.userCanDismissLockScreen + } + } + + /** + * Returns the content of the buffer (sorted from latest to newest). + * + * @see KeyguardFingerprintListenModel.asStringList + */ + fun toList(): List<Row> { + return buffer.asSequence().map { it.asStringList }.toList() + } + } + + companion object { + const val CAPACITY = 20 // number of logs to retain + + /** Headers for dumping a table using [DumpsysTableLogger]. */ + @JvmField + val TABLE_HEADERS = + listOf( + "timestamp", + "time_millis", + "userId", + "listening", + // keep sorted + "awakeKeyguard", + "authInterruptActive", + "fpLockedOut", + "primaryAuthRequired", + "switchingUser", + "triggerActiveUnlockForAssistant", + "userCanDismissLockScreen", + ) + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt new file mode 100644 index 000000000000..deead1959b8a --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt @@ -0,0 +1,162 @@ +/* + * 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.keyguard + +import android.annotation.CurrentTimeMillisLong +import com.android.systemui.dump.DumpsysTableLogger +import com.android.systemui.dump.Row +import com.android.systemui.plugins.util.RingBuffer + +/** Verbose debug information associated. */ +data class KeyguardFaceListenModel( + @CurrentTimeMillisLong override var timeMillis: Long = 0L, + override var userId: Int = 0, + override var listening: Boolean = false, + // keep sorted + var authInterruptActive: Boolean = false, + var biometricSettingEnabledForUser: Boolean = false, + var bouncerFullyShown: Boolean = false, + var faceAndFpNotAuthenticated: Boolean = false, + var faceAuthAllowed: Boolean = false, + var faceDisabled: Boolean = false, + var faceLockedOut: Boolean = false, + var goingToSleep: Boolean = false, + var keyguardAwake: Boolean = false, + var keyguardGoingAway: Boolean = false, + var listeningForFaceAssistant: Boolean = false, + var occludingAppRequestingFaceAuth: Boolean = false, + var primaryUser: Boolean = false, + var secureCameraLaunched: Boolean = false, + var supportsDetect: Boolean = false, + var switchingUser: Boolean = false, + var udfpsBouncerShowing: Boolean = false, + var udfpsFingerDown: Boolean = false, + var userNotTrustedOrDetectionIsNeeded: Boolean = false, +) : KeyguardListenModel() { + + /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */ + val asStringList: List<String> by lazy { + listOf( + DATE_FORMAT.format(timeMillis), + timeMillis.toString(), + userId.toString(), + listening.toString(), + // keep sorted + authInterruptActive.toString(), + biometricSettingEnabledForUser.toString(), + bouncerFullyShown.toString(), + faceAndFpNotAuthenticated.toString(), + faceAuthAllowed.toString(), + faceDisabled.toString(), + faceLockedOut.toString(), + goingToSleep.toString(), + keyguardAwake.toString(), + keyguardGoingAway.toString(), + listeningForFaceAssistant.toString(), + occludingAppRequestingFaceAuth.toString(), + primaryUser.toString(), + secureCameraLaunched.toString(), + supportsDetect.toString(), + switchingUser.toString(), + udfpsBouncerShowing.toString(), + udfpsFingerDown.toString(), + userNotTrustedOrDetectionIsNeeded.toString(), + ) + } + + /** + * [RingBuffer] to store [KeyguardFaceListenModel]. After the buffer is full, it will recycle + * old events. + * + * Do not use [append] to add new elements. Instead use [insert], as it will recycle if + * necessary. + */ + class Buffer { + private val buffer = RingBuffer(CAPACITY) { KeyguardFaceListenModel() } + + fun insert(model: KeyguardFaceListenModel) { + buffer.advance().apply { + timeMillis = model.timeMillis + userId = model.userId + listening = model.listening + // keep sorted + biometricSettingEnabledForUser = model.biometricSettingEnabledForUser + bouncerFullyShown = model.bouncerFullyShown + faceAndFpNotAuthenticated = model.faceAndFpNotAuthenticated + faceAuthAllowed = model.faceAuthAllowed + faceDisabled = model.faceDisabled + faceLockedOut = model.faceLockedOut + goingToSleep = model.goingToSleep + keyguardAwake = model.keyguardAwake + goingToSleep = model.goingToSleep + keyguardGoingAway = model.keyguardGoingAway + listeningForFaceAssistant = model.listeningForFaceAssistant + occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth + primaryUser = model.primaryUser + secureCameraLaunched = model.secureCameraLaunched + supportsDetect = model.supportsDetect + switchingUser = model.switchingUser + udfpsBouncerShowing = model.udfpsBouncerShowing + switchingUser = model.switchingUser + udfpsFingerDown = model.udfpsFingerDown + userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded + } + } + /** + * Returns the content of the buffer (sorted from latest to newest). + * + * @see KeyguardFingerprintListenModel.asStringList + */ + fun toList(): List<Row> { + return buffer.asSequence().map { it.asStringList }.toList() + } + } + + companion object { + const val CAPACITY = 40 // number of logs to retain + + /** Headers for dumping a table using [DumpsysTableLogger]. */ + @JvmField + val TABLE_HEADERS = + listOf( + "timestamp", + "time_millis", + "userId", + "listening", + // keep sorted + "authInterruptActive", + "biometricSettingEnabledForUser", + "bouncerFullyShown", + "faceAndFpNotAuthenticated", + "faceAuthAllowed", + "faceDisabled", + "faceLockedOut", + "goingToSleep", + "keyguardAwake", + "keyguardGoingAway", + "listeningForFaceAssistant", + "occludingAppRequestingFaceAuth", + "primaryUser", + "secureCameraLaunched", + "supportsDetect", + "switchingUser", + "udfpsBouncerShowing", + "udfpsFingerDown", + "userNotTrustedOrDetectionIsNeeded", + ) + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt new file mode 100644 index 000000000000..998dc09aa5ab --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt @@ -0,0 +1,166 @@ +/* + * 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.keyguard + +import android.annotation.CurrentTimeMillisLong +import com.android.systemui.dump.DumpsysTableLogger +import com.android.systemui.dump.Row +import com.android.systemui.plugins.util.RingBuffer + +/** Verbose debug information. */ +data class KeyguardFingerprintListenModel( + @CurrentTimeMillisLong override var timeMillis: Long = 0L, + override var userId: Int = 0, + override var listening: Boolean = false, + // keepSorted + var biometricEnabledForUser: Boolean = false, + var bouncerIsOrWillShow: Boolean = false, + var canSkipBouncer: Boolean = false, + var credentialAttempted: Boolean = false, + var deviceInteractive: Boolean = false, + var dreaming: Boolean = false, + var fingerprintDisabled: Boolean = false, + var fingerprintLockedOut: Boolean = false, + var goingToSleep: Boolean = false, + var keyguardGoingAway: Boolean = false, + var keyguardIsVisible: Boolean = false, + var keyguardOccluded: Boolean = false, + var occludingAppRequestingFp: Boolean = false, + var primaryUser: Boolean = false, + var shouldListenSfpsState: Boolean = false, + var shouldListenForFingerprintAssistant: Boolean = false, + var strongerAuthRequired: Boolean = false, + var switchingUser: Boolean = false, + var udfps: Boolean = false, + var userDoesNotHaveTrust: Boolean = false, +) : KeyguardListenModel() { + + /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */ + val asStringList: List<String> by lazy { + listOf( + DATE_FORMAT.format(timeMillis), + timeMillis.toString(), + userId.toString(), + listening.toString(), + // keep sorted + biometricEnabledForUser.toString(), + bouncerIsOrWillShow.toString(), + canSkipBouncer.toString(), + credentialAttempted.toString(), + deviceInteractive.toString(), + dreaming.toString(), + fingerprintDisabled.toString(), + fingerprintLockedOut.toString(), + goingToSleep.toString(), + keyguardGoingAway.toString(), + keyguardIsVisible.toString(), + keyguardOccluded.toString(), + occludingAppRequestingFp.toString(), + primaryUser.toString(), + shouldListenSfpsState.toString(), + shouldListenForFingerprintAssistant.toString(), + strongerAuthRequired.toString(), + switchingUser.toString(), + udfps.toString(), + userDoesNotHaveTrust.toString(), + ) + } + + /** + * [RingBuffer] to store [KeyguardFingerprintListenModel]. After the buffer is full, it will + * recycle old events. + * + * Do not use [append] to add new elements. Instead use [insert], as it will recycle if + * necessary. + */ + class Buffer { + private val buffer = RingBuffer(CAPACITY) { KeyguardFingerprintListenModel() } + + fun insert(model: KeyguardFingerprintListenModel) { + buffer.advance().apply { + timeMillis = model.timeMillis + userId = model.userId + listening = model.listening + // keep sorted + biometricEnabledForUser = model.biometricEnabledForUser + bouncerIsOrWillShow = model.bouncerIsOrWillShow + canSkipBouncer = model.canSkipBouncer + credentialAttempted = model.credentialAttempted + deviceInteractive = model.deviceInteractive + dreaming = model.dreaming + fingerprintDisabled = model.fingerprintDisabled + fingerprintLockedOut = model.fingerprintLockedOut + goingToSleep = model.goingToSleep + keyguardGoingAway = model.keyguardGoingAway + keyguardIsVisible = model.keyguardIsVisible + keyguardOccluded = model.keyguardOccluded + occludingAppRequestingFp = model.occludingAppRequestingFp + primaryUser = model.primaryUser + shouldListenSfpsState = model.shouldListenSfpsState + shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant + strongerAuthRequired = model.strongerAuthRequired + switchingUser = model.switchingUser + udfps = model.udfps + userDoesNotHaveTrust = model.userDoesNotHaveTrust + } + } + + /** + * Returns the content of the buffer (sorted from latest to newest). + * + * @see KeyguardFingerprintListenModel.asStringList + */ + fun toList(): List<Row> { + return buffer.asSequence().map { it.asStringList }.toList() + } + } + + companion object { + const val CAPACITY = 20 // number of logs to retain + + /** Headers for dumping a table using [DumpsysTableLogger]. */ + @JvmField + val TABLE_HEADERS = + listOf( + "timestamp", + "time_millis", + "userId", + "listening", + // keep sorted + "biometricAllowedForUser", + "bouncerIsOrWillShow", + "canSkipBouncer", + "credentialAttempted", + "deviceInteractive", + "dreaming", + "fingerprintDisabled", + "fingerprintLockedOut", + "goingToSleep", + "keyguardGoingAway", + "keyguardIsVisible", + "keyguardOccluded", + "occludingAppRequestingFp", + "primaryUser", + "shouldListenSidFingerprintState", + "shouldListenForFingerprintAssistant", + "strongAuthRequired", + "switchingUser", + "underDisplayFingerprint", + "userDoesNotHaveTrust", + ) + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 2b660dee4f16..d4ca8e34fb32 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -67,8 +67,12 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override - public void onTrustGrantedForCurrentUser(boolean dismissKeyguard, - TrustGrantFlags flags, String message) { + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + TrustGrantFlags flags, + String message + ) { if (dismissKeyguard) { if (!mView.isVisibleToUser()) { // The trust agent dismissed the keyguard without the user proving diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt index 52ca1668e4c9..1296cf3b1f33 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt @@ -1,87 +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.keyguard -import android.annotation.CurrentTimeMillisLong +import java.text.SimpleDateFormat +import java.util.Locale /** Verbose logging for various keyguard listening states. */ sealed class KeyguardListenModel { /** Timestamp of the state change. */ - abstract val timeMillis: Long + abstract var timeMillis: Long /** Current user. */ - abstract val userId: Int + abstract var userId: Int /** If keyguard is listening for the modality represented by this model. */ - abstract val listening: Boolean + abstract var listening: Boolean } -/** - * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFingerprint]. - */ -data class KeyguardFingerprintListenModel( - @CurrentTimeMillisLong override val timeMillis: Long, - override val userId: Int, - override val listening: Boolean, - // keep sorted - val biometricEnabledForUser: Boolean, - val bouncerIsOrWillShow: Boolean, - val canSkipBouncer: Boolean, - val credentialAttempted: Boolean, - val deviceInteractive: Boolean, - val dreaming: Boolean, - val fingerprintDisabled: Boolean, - val fingerprintLockedOut: Boolean, - val goingToSleep: Boolean, - val keyguardGoingAway: Boolean, - val keyguardIsVisible: Boolean, - val keyguardOccluded: Boolean, - val occludingAppRequestingFp: Boolean, - val primaryUser: Boolean, - val shouldListenSfpsState: Boolean, - val shouldListenForFingerprintAssistant: Boolean, - val strongerAuthRequired: Boolean, - val switchingUser: Boolean, - val udfps: Boolean, - val userDoesNotHaveTrust: Boolean -) : KeyguardListenModel() -/** - * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFace]. - */ -data class KeyguardFaceListenModel( - @CurrentTimeMillisLong override val timeMillis: Long, - override val userId: Int, - override val listening: Boolean, - // keep sorted - val authInterruptActive: Boolean, - val biometricSettingEnabledForUser: Boolean, - val bouncerFullyShown: Boolean, - val faceAndFpNotAuthenticated: Boolean, - val faceAuthAllowed: Boolean, - val faceDisabled: Boolean, - val faceLockedOut: Boolean, - val goingToSleep: Boolean, - val keyguardAwake: Boolean, - val keyguardGoingAway: Boolean, - val listeningForFaceAssistant: Boolean, - val occludingAppRequestingFaceAuth: Boolean, - val primaryUser: Boolean, - val secureCameraLaunched: Boolean, - val supportsDetect: Boolean, - val switchingUser: Boolean, - val udfpsBouncerShowing: Boolean, - val udfpsFingerDown: Boolean, - val userNotTrustedOrDetectionIsNeeded: Boolean, - ) : KeyguardListenModel() -/** - * Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock]. - */ -data class KeyguardActiveUnlockModel( - @CurrentTimeMillisLong override val timeMillis: Long, - override val userId: Int, - override val listening: Boolean, - // keep sorted - val awakeKeyguard: Boolean, - val authInterruptActive: Boolean, - val fpLockedOut: Boolean, - val primaryAuthRequired: Boolean, - val switchingUser: Boolean, - val triggerActiveUnlockForAssistant: Boolean, - val userCanDismissLockScreen: Boolean -) : KeyguardListenModel() +val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt deleted file mode 100644 index 210f5e763911..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt +++ /dev/null @@ -1,73 +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.keyguard - -import androidx.annotation.VisibleForTesting -import java.io.PrintWriter -import java.text.DateFormat -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import kotlin.collections.ArrayDeque - -private val DEFAULT_FORMATTING = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US) - -/** Queue for verbose logging checks for the listening state. */ -class KeyguardListenQueue( - val sizePerModality: Int = 20 -) { - private val faceQueue = ArrayDeque<KeyguardFaceListenModel>() - private val fingerprintQueue = ArrayDeque<KeyguardFingerprintListenModel>() - private val activeUnlockQueue = ArrayDeque<KeyguardActiveUnlockModel>() - - @get:VisibleForTesting val models: List<KeyguardListenModel> - get() = faceQueue + fingerprintQueue + activeUnlockQueue - - /** Push a [model] to the queue (will be logged until the queue exceeds [sizePerModality]). */ - fun add(model: KeyguardListenModel) { - val queue = when (model) { - is KeyguardFaceListenModel -> faceQueue.apply { add(model) } - is KeyguardFingerprintListenModel -> fingerprintQueue.apply { add(model) } - is KeyguardActiveUnlockModel -> activeUnlockQueue.apply { add(model) } - } - - if (queue.size > sizePerModality) { - queue.removeFirstOrNull() - } - } - - /** Print verbose logs via the [writer]. */ - @JvmOverloads - fun print(writer: PrintWriter, dateFormat: DateFormat = DEFAULT_FORMATTING) { - val stringify: (KeyguardListenModel) -> String = { model -> - " ${dateFormat.format(Date(model.timeMillis))} $model" - } - - writer.println(" Face listen results (last ${faceQueue.size} calls):") - for (model in faceQueue) { - writer.println(stringify(model)) - } - writer.println(" Fingerprint listen results (last ${fingerprintQueue.size} calls):") - for (model in fingerprintQueue) { - writer.println(stringify(model)) - } - writer.println(" Active unlock triggers (last ${activeUnlockQueue.size} calls):") - for (model in activeUnlockQueue) { - writer.println(stringify(model)) - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 67e3400670ba..061ca4f7d850 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -177,8 +177,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { @Override public void startAppearAnimation() { - setAlpha(1f); - setTranslationY(0); if (mAppearAnimator.isRunning()) { mAppearAnimator.cancel(); } @@ -215,6 +213,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { /** Animate subviews according to expansion or time. */ private void animate(float progress) { + setAlpha(progress); Interpolator standardDecelerate = Interpolators.STANDARD_DECELERATE; Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index c985fd7bef82..c1fae9e44bd3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -24,6 +24,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.animation.Animator; @@ -107,6 +108,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; + case PROMPT_REASON_TRUSTAGENT_EXPIRED: + return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 571d2740773d..0c1748982e51 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -313,6 +313,9 @@ public class KeyguardPatternViewController case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); break; + case PROMPT_REASON_TRUSTAGENT_EXPIRED: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; case PROMPT_REASON_NONE: break; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index c46e33d9fd53..0a91150e6c39 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -22,6 +22,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.animation.Animator; @@ -123,6 +124,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; + case PROMPT_REASON_TRUSTAGENT_EXPIRED: + return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index ac00e9453c97..67d77e53738a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -61,6 +61,12 @@ public interface KeyguardSecurityView { int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7; /** + * Some auth is required because the trustagent expired either from timeout or manually by the + * user + */ + int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8; + + /** * Reset the view and prepare to take input. This should do things like clearing the * password or pattern and clear error messages. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index ec4b78065c63..2c3d3a0e0a64 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -146,6 +146,7 @@ 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.DumpsysTableLogger; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; @@ -461,14 +462,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray(); private final Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<>(); + private final KeyguardFingerprintListenModel.Buffer mFingerprintListenBuffer = + new KeyguardFingerprintListenModel.Buffer(); + private final KeyguardFaceListenModel.Buffer mFaceListenBuffer = + new KeyguardFaceListenModel.Buffer(); + private final KeyguardActiveUnlockModel.Buffer mActiveUnlockTriggerBuffer = + new KeyguardActiveUnlockModel.Buffer(); + @VisibleForTesting SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); @VisibleForTesting SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); - // Keep track of recent calls to shouldListenFor*() for debugging. - private final KeyguardListenQueue mListenModels = new KeyguardListenQueue(); - private static int sCurrentUser; public synchronized static void setCurrentUser(int currentUser) { @@ -480,7 +485,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } @Override - public void onTrustChanged(boolean enabled, int userId, int flags, + public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags, List<String> trustGrantedMessages) { Assert.isMainThread(); boolean wasTrusted = mUserHasTrust.get(userId, false); @@ -510,7 +515,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - mLogger.logTrustGrantedWithFlags(flags, userId, message); + mLogger.logTrustGrantedWithFlags(flags, newlyUnlocked, userId, message); if (userId == getCurrentUser()) { final TrustGrantFlags trustGrantFlags = new TrustGrantFlags(flags); for (int i = 0; i < mCallbacks.size(); i++) { @@ -518,7 +523,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (cb != null) { cb.onTrustGrantedForCurrentUser( shouldDismissKeyguardOnTrustGrantedWithCurrentUser(trustGrantFlags), - trustGrantFlags, message); + newlyUnlocked, + trustGrantFlags, + message + ); } } } @@ -2647,7 +2655,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && !mSecureCameraLaunched; // Aggregate relevant fields for debug logging. - maybeLogListenerModelData( + logListenerModelData( new KeyguardActiveUnlockModel( System.currentTimeMillis(), user, @@ -2728,7 +2736,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean shouldListen = shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState && shouldListenSideFpsState; - maybeLogListenerModelData( + logListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), user, @@ -2813,7 +2821,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && !mGoingToSleep; // Aggregate relevant fields for debug logging. - maybeLogListenerModelData( + logListenerModelData( new KeyguardFaceListenModel( System.currentTimeMillis(), user, @@ -2841,28 +2849,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return shouldListen; } - private void maybeLogListenerModelData(@NonNull KeyguardListenModel model) { + private void logListenerModelData(@NonNull KeyguardListenModel model) { mLogger.logKeyguardListenerModel(model); - - if (model instanceof KeyguardActiveUnlockModel) { - mListenModels.add(model); - return; - } - - // Add model data to the historical buffer. - final boolean notYetRunning = - (model instanceof KeyguardFaceListenModel - && mFaceRunningState != BIOMETRIC_STATE_RUNNING) - || (model instanceof KeyguardFingerprintListenModel - && mFingerprintRunningState != BIOMETRIC_STATE_RUNNING); - final boolean running = - (model instanceof KeyguardFaceListenModel - && mFaceRunningState == BIOMETRIC_STATE_RUNNING) - || (model instanceof KeyguardFingerprintListenModel - && mFingerprintRunningState == BIOMETRIC_STATE_RUNNING); - if (notYetRunning && model.getListening() - || running && !model.getListening()) { - mListenModels.add(model); + if (model instanceof KeyguardFingerprintListenModel) { + mFingerprintListenBuffer.insert((KeyguardFingerprintListenModel) model); + } else if (model instanceof KeyguardActiveUnlockModel) { + mActiveUnlockTriggerBuffer.insert((KeyguardActiveUnlockModel) model); + } else if (model instanceof KeyguardFaceListenModel) { + mFaceListenBuffer.insert((KeyguardFaceListenModel) model); } } @@ -3935,6 +3929,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" mSfpsRequireScreenOnToAuthPrefEnabled=" + mSfpsRequireScreenOnToAuthPrefEnabled); } + new DumpsysTableLogger( + "KeyguardFingerprintListen", + KeyguardFingerprintListenModel.TABLE_HEADERS, + mFingerprintListenBuffer.toList() + ).printTableData(pw); } if (mFaceManager != null && mFaceManager.isHardwareDetected()) { final int userId = mUserTracker.getUserId(); @@ -3960,7 +3959,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched); pw.println(" mPrimaryBouncerFullyShown=" + mPrimaryBouncerFullyShown); pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition); - } - mListenModels.print(pw); + new DumpsysTableLogger( + "KeyguardFaceListen", + KeyguardFaceListenModel.TABLE_HEADERS, + mFaceListenBuffer.toList() + ).printTableData(pw); + } + + new DumpsysTableLogger( + "KeyguardActiveUnlockTriggers", + KeyguardActiveUnlockModel.TABLE_HEADERS, + mActiveUnlockTriggerBuffer.toList() + ).printTableData(pw); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 1d58fc9cf94b..e6b9ac8b38a8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -178,11 +178,17 @@ public class KeyguardUpdateMonitorCallback { * Called after trust was granted. * @param dismissKeyguard whether the keyguard should be dismissed as a result of the * trustGranted + * @param newlyUnlocked whether the grantedTrust is believed to be the cause of a newly + * unlocked device (after being locked). * @param message optional message the trust agent has provided to show that should indicate * why trust was granted. */ - public void onTrustGrantedForCurrentUser(boolean dismissKeyguard, - @NonNull TrustGrantFlags flags, @Nullable String message) { } + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + @NonNull TrustGrantFlags flags, + @Nullable String message + ) { } /** * Called when a biometric has been acquired. diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index ceebe4c69091..21d3b24174b6 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -379,14 +379,17 @@ class KeyguardUpdateMonitorLogger @Inject constructor( fun logTrustGrantedWithFlags( flags: Int, + newlyUnlocked: Boolean, userId: Int, message: String? ) { logBuffer.log(TAG, DEBUG, { int1 = flags + bool1 = newlyUnlocked int2 = userId str1 = message - }, { "trustGrantedWithFlags[user=$int2] flags=${TrustGrantFlags(int1)} message=$str1" }) + }, { "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " + + "flags=${TrustGrantFlags(int1)} message=$str1" }) } fun logTrustChanged( diff --git a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt index 4b7e9a57ef6a..98ac2c0bd026 100644 --- a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt +++ b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt @@ -16,31 +16,25 @@ package com.android.keyguard.mediator +import android.annotation.BinderThread import android.os.Trace - import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.ScreenLifecycle -import com.android.systemui.util.concurrency.Execution -import com.android.systemui.util.concurrency.PendingTasksContainer import com.android.systemui.unfold.SysUIUnfoldComponent +import com.android.systemui.util.concurrency.PendingTasksContainer import com.android.systemui.util.kotlin.getOrNull - import java.util.Optional - import javax.inject.Inject /** * Coordinates screen on/turning on animations for the KeyguardViewMediator. Specifically for * screen on events, this will invoke the onDrawn Runnable after all tasks have completed. This - * should route back to the KeyguardService, which informs the system_server that keyguard has - * drawn. + * should route back to the [com.android.systemui.keyguard.KeyguardService], which informs + * the system_server that keyguard has drawn. */ @SysUISingleton class ScreenOnCoordinator @Inject constructor( - screenLifecycle: ScreenLifecycle, - unfoldComponent: Optional<SysUIUnfoldComponent>, - private val execution: Execution -) : ScreenLifecycle.Observer { + unfoldComponent: Optional<SysUIUnfoldComponent> +) { private val unfoldLightRevealAnimation = unfoldComponent.map( SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation).getOrNull() @@ -48,15 +42,12 @@ class ScreenOnCoordinator @Inject constructor( SysUIUnfoldComponent::getFoldAodAnimationController).getOrNull() private val pendingTasks = PendingTasksContainer() - init { - screenLifecycle.addObserver(this) - } - /** * When turning on, registers tasks that may need to run before invoking [onDrawn]. + * This is called on a binder thread from [com.android.systemui.keyguard.KeyguardService]. */ - override fun onScreenTurningOn(onDrawn: Runnable) { - execution.assertIsMainThread() + @BinderThread + fun onScreenTurningOn(onDrawn: Runnable) { Trace.beginSection("ScreenOnCoordinator#onScreenTurningOn") pendingTasks.reset() @@ -68,11 +59,13 @@ class ScreenOnCoordinator @Inject constructor( Trace.endSection() } - override fun onScreenTurnedOn() { - execution.assertIsMainThread() - + /** + * Called when screen is fully turned on and screen on blocker is removed. + * This is called on a binder thread from [com.android.systemui.keyguard.KeyguardService]. + */ + @BinderThread + fun onScreenTurnedOn() { foldAodAnimationController?.onScreenTurnedOn() - pendingTasks.reset() } } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 63c20651fcd5..f97d6af632b0 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON; +import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED; @@ -41,7 +42,6 @@ import android.app.RemoteAction; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ClipDescription; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -51,7 +51,6 @@ import android.graphics.Bitmap; import android.hardware.display.DisplayManager; import android.hardware.input.InputManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Looper; import android.provider.DeviceConfig; import android.text.TextUtils; @@ -62,10 +61,6 @@ import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputMonitor; import android.view.MotionEvent; -import android.view.textclassifier.TextClassification; -import android.view.textclassifier.TextClassificationManager; -import android.view.textclassifier.TextClassifier; -import android.view.textclassifier.TextLinks; import androidx.annotation.NonNull; @@ -74,12 +69,13 @@ import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.screenshot.TimeoutHandler; import java.io.IOException; -import java.util.ArrayList; import java.util.Optional; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -102,9 +98,9 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final DisplayManager mDisplayManager; private final ClipboardOverlayWindow mWindow; private final TimeoutHandler mTimeoutHandler; - private final TextClassifier mTextClassifier; private final ClipboardOverlayUtils mClipboardUtils; private final FeatureFlags mFeatureFlags; + private final Executor mBgExecutor; private final ClipboardOverlayView mView; @@ -189,6 +185,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv TimeoutHandler timeoutHandler, FeatureFlags featureFlags, ClipboardOverlayUtils clipboardUtils, + @Background Executor bgExecutor, UiEventLogger uiEventLogger) { mBroadcastDispatcher = broadcastDispatcher; mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class)); @@ -204,14 +201,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv hideImmediate(); }); - mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class)) - .getTextClassifier(); - mTimeoutHandler = timeoutHandler; mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS); mFeatureFlags = featureFlags; mClipboardUtils = clipboardUtils; + mBgExecutor = bgExecutor; mView.setCallbacks(mClipboardCallbacks); @@ -281,7 +276,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv if (isRemote || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) { if (item.getTextLinks() != null) { - AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource)); + classifyText(clipData.getItemAt(0), clipSource); } } if (isSensitive) { @@ -338,22 +333,18 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv } private void classifyText(ClipData.Item item, String source) { - ArrayList<RemoteAction> actions = new ArrayList<>(); - for (TextLinks.TextLink link : item.getTextLinks().getLinks()) { - TextClassification classification = mTextClassifier.classifyText( - item.getText(), link.getStart(), link.getEnd(), null); - actions.addAll(classification.getActions()); - } - mView.post(() -> { - Optional<RemoteAction> action = actions.stream().filter(remoteAction -> { - ComponentName component = remoteAction.getActionIntent().getIntent().getComponent(); - return component != null && !TextUtils.equals(source, component.getPackageName()); - }).findFirst(); - mView.resetActionChips(); - action.ifPresent(remoteAction -> mView.setActionChip(remoteAction, () -> { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); - animateOut(); - })); + mBgExecutor.execute(() -> { + Optional<RemoteAction> action = mClipboardUtils.getAction(item, source); + mView.post(() -> { + mView.resetActionChips(); + action.ifPresent(remoteAction -> { + mView.setActionChip(remoteAction, () -> { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); + animateOut(); + }); + mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN); + }); + }); }); } @@ -539,6 +530,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipSource = clipSource; } + void logUnguarded(@NonNull UiEventLogger.UiEventEnum event) { + mUiEventLogger.log(event, 0, mClipSource); + } + void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) { if (!mGuarded) { mGuarded = true; diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayEvent.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayEvent.java index a0b2ab99e240..9917507ec3bf 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayEvent.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayEvent.java @@ -28,6 +28,8 @@ public enum ClipboardOverlayEvent implements UiEventLogger.UiEventEnum { CLIPBOARD_OVERLAY_EDIT_TAPPED(951), @UiEvent(doc = "clipboard share tapped") CLIPBOARD_OVERLAY_SHARE_TAPPED(1067), + @UiEvent(doc = "clipboard smart action shown") + CLIPBOARD_OVERLAY_ACTION_SHOWN(1260), @UiEvent(doc = "clipboard action tapped") CLIPBOARD_OVERLAY_ACTION_TAPPED(952), @UiEvent(doc = "clipboard remote copy tapped") diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java index c194e66f8e92..785e4a0743e4 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java @@ -16,22 +16,34 @@ package com.android.systemui.clipboardoverlay; +import android.app.RemoteAction; import android.content.ClipData; import android.content.ClipDescription; import android.content.ComponentName; import android.content.Context; import android.os.Build; import android.provider.DeviceConfig; +import android.text.TextUtils; +import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationManager; +import android.view.textclassifier.TextClassifier; +import android.view.textclassifier.TextLinks; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; +import java.util.ArrayList; +import java.util.Optional; + import javax.inject.Inject; class ClipboardOverlayUtils { + private final TextClassifier mTextClassifier; + @Inject - ClipboardOverlayUtils() { + ClipboardOverlayUtils(TextClassificationManager textClassificationManager) { + mTextClassifier = textClassificationManager.getTextClassifier(); } boolean isRemoteCopy(Context context, ClipData clipData, String clipSource) { @@ -52,4 +64,21 @@ class ClipboardOverlayUtils { } return false; } + + public Optional<RemoteAction> getAction(ClipData.Item item, String source) { + return getActions(item).stream().filter(remoteAction -> { + ComponentName component = remoteAction.getActionIntent().getIntent().getComponent(); + return component != null && !TextUtils.equals(source, component.getPackageName()); + }).findFirst(); + } + + private ArrayList<RemoteAction> getActions(ClipData.Item item) { + ArrayList<RemoteAction> actions = new ArrayList<>(); + for (TextLinks.TextLink link : item.getTextLinks().getLinks()) { + TextClassification classification = mTextClassifier.classifyText( + item.getText(), link.getStart(), link.getEnd(), null); + actions.addAll(classification.getActions()); + } + return actions; + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 7864f1901e57..f902252fa2e6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -92,6 +92,9 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; import android.view.inputmethod.InputMethodManager; +import android.view.textclassifier.TextClassificationManager; + +import androidx.core.app.NotificationManagerCompat; import com.android.internal.app.IBatteryStats; import com.android.internal.appwidget.IAppWidgetService; @@ -389,6 +392,12 @@ public class FrameworkServicesModule { return context.getSystemService(NotificationManager.class); } + @Provides + @Singleton + static NotificationManagerCompat provideNotificationManagerCompat(Context context) { + return NotificationManagerCompat.from(context); + } + /** */ @Provides @Singleton @@ -623,4 +632,10 @@ public class FrameworkServicesModule { static BluetoothAdapter provideBluetoothAdapter(BluetoothManager bluetoothManager) { return bluetoothManager.getAdapter(); } + + @Provides + @Singleton + static TextClassificationManager provideTextClassificationManager(Context context) { + return context.getSystemService(TextClassificationManager.class); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 0fbe0acb5642..30117d988091 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -40,8 +40,10 @@ import com.android.systemui.reardisplay.RearDisplayDialogController 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.fsi.FsiChromeRepo import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController +import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.theme.ThemeOverlayController import com.android.systemui.toast.ToastUI @@ -78,6 +80,12 @@ abstract class SystemUICoreStartableModule { @ClassKey(ClipboardListener::class) abstract fun bindClipboardListener(sysui: ClipboardListener): CoreStartable + /** Inject into FsiChromeRepo. */ + @Binds + @IntoMap + @ClassKey(FsiChromeRepo::class) + abstract fun bindFSIChromeRepo(sysui: FsiChromeRepo): CoreStartable + /** Inject into GarbageMonitor.Service. */ @Binds @IntoMap @@ -251,4 +259,10 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(RearDisplayDialogController::class) abstract fun bindRearDisplayDialogController(sysui: RearDisplayDialogController): CoreStartable + + /** Inject into StylusUsiPowerStartable) */ + @Binds + @IntoMap + @ClassKey(StylusUsiPowerStartable::class) + abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index efb513d57db6..ee9b804ec30f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -165,7 +165,9 @@ object Flags { /** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */ // TODO(b/256513609): Tracking Bug - @JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar") + @JvmField + val ACTIVE_UNLOCK_CHIPBAR = + resourceBooleanFlag(217, R.bool.flag_active_unlock_chipbar, "active_unlock_chipbar") /** * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the @@ -396,6 +398,10 @@ object Flags { // TODO(b/254512756): Tracking Bug val QUICK_TAP_IN_PCC = releasedFlag(1400, "quick_tap_in_pcc") + // TODO(b/261979569): Tracking Bug + val QUICK_TAP_FLOW_FRAMEWORK = + unreleasedFlag(1401, "quick_tap_flow_framework", teamfood = false) + // 1500 - chooser // TODO(b/254512507): Tracking Bug val CHOOSER_UNBUNDLED = unreleasedFlag(1500, "chooser_unbundled", teamfood = true) @@ -449,6 +455,11 @@ object Flags { // TODO(b/261538825): Tracking Bug @JvmField val OUTPUT_SWITCHER_ADVANCED_LAYOUT = unreleasedFlag(2500, "output_switcher_advanced_layout") + @JvmField + val OUTPUT_SWITCHER_ROUTES_PROCESSING = + unreleasedFlag(2501, "output_switcher_routes_processing") + @JvmField + val OUTPUT_SWITCHER_DEVICE_STATUS = unreleasedFlag(2502, "output_switcher_device_status") // TODO(b259590361): Tracking bug val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java index 822b1cfdf877..757afb616fd1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java @@ -18,11 +18,8 @@ package com.android.systemui.keyguard; import android.os.Handler; import android.os.Message; -import android.os.RemoteException; import android.os.Trace; -import android.util.Log; -import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; @@ -80,33 +77,10 @@ public class KeyguardLifecyclesDispatcher { private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { - final Object obj = msg.obj; switch (msg.what) { case SCREEN_TURNING_ON: Trace.beginSection("KeyguardLifecyclesDispatcher#SCREEN_TURNING_ON"); - final String onDrawWaitingTraceTag = - "Waiting for KeyguardDrawnCallback#onDrawn"; - int traceCookie = System.identityHashCode(msg); - Trace.beginAsyncSection(onDrawWaitingTraceTag, traceCookie); - // Ensure the drawn callback is only ever called once - mScreenLifecycle.dispatchScreenTurningOn(new Runnable() { - boolean mInvoked; - @Override - public void run() { - if (obj == null) return; - if (!mInvoked) { - mInvoked = true; - try { - Trace.endAsyncSection(onDrawWaitingTraceTag, traceCookie); - ((IKeyguardDrawnCallback) obj).onDrawn(); - } catch (RemoteException e) { - Log.w(TAG, "Exception calling onDrawn():", e); - } - } else { - Log.w(TAG, "KeyguardDrawnCallback#onDrawn() invoked > 1 times"); - } - } - }); + mScreenLifecycle.dispatchScreenTurningOn(); Trace.endSection(); break; case SCREEN_TURNED_ON: diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index c332a0d66294..f4a1227a467c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -78,6 +78,7 @@ import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; import com.android.internal.policy.IKeyguardStateCallback; +import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.SystemUIApplication; import com.android.wm.shell.transition.ShellTransitions; import com.android.wm.shell.transition.Transitions; @@ -120,6 +121,7 @@ public class KeyguardService extends Service { private final KeyguardViewMediator mKeyguardViewMediator; private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher; + private final ScreenOnCoordinator mScreenOnCoordinator; private final ShellTransitions mShellTransitions; private static int newModeToLegacyMode(int newMode) { @@ -283,10 +285,12 @@ public class KeyguardService extends Service { @Inject public KeyguardService(KeyguardViewMediator keyguardViewMediator, KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher, + ScreenOnCoordinator screenOnCoordinator, ShellTransitions shellTransitions) { super(); mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; + mScreenOnCoordinator = screenOnCoordinator; mShellTransitions = shellTransitions; } @@ -583,6 +587,31 @@ public class KeyguardService extends Service { checkPermission(); mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNING_ON, callback); + + final String onDrawWaitingTraceTag = "Waiting for KeyguardDrawnCallback#onDrawn"; + final int traceCookie = System.identityHashCode(callback); + Trace.beginAsyncSection(onDrawWaitingTraceTag, traceCookie); + + // Ensure the drawn callback is only ever called once + mScreenOnCoordinator.onScreenTurningOn(new Runnable() { + boolean mInvoked; + @Override + public void run() { + if (callback == null) return; + if (!mInvoked) { + mInvoked = true; + try { + Trace.endAsyncSection(onDrawWaitingTraceTag, traceCookie); + callback.onDrawn(); + } catch (RemoteException e) { + Log.w(TAG, "Exception calling onDrawn():", e); + } + } else { + Log.w(TAG, "KeyguardDrawnCallback#onDrawn() invoked > 1 times"); + } + } + }); + Trace.endSection(); } @@ -591,6 +620,7 @@ public class KeyguardService extends Service { Trace.beginSection("KeyguardService.mBinder#onScreenTurnedOn"); checkPermission(); mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNED_ON); + mScreenOnCoordinator.onScreenTurnedOn(); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index d4b6386ff706..d231870997f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -25,6 +25,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BA import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -142,12 +143,12 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; +import dagger.Lazy; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.concurrent.Executor; -import dagger.Lazy; - /** * Mediates requests related to the keyguard. This includes queries about the * state of the keyguard, power management events that effect whether the keyguard @@ -722,7 +723,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, @Override public void keyguardDone(boolean strongAuth, int targetUserId) { - if (targetUserId != mUserTracker.getUserId()) { + if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) { return; } if (DEBUG) Log.d(TAG, "keyguardDone"); @@ -745,7 +746,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void keyguardDonePending(boolean strongAuth, int targetUserId) { Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending"); if (DEBUG) Log.d(TAG, "keyguardDonePending"); - if (targetUserId != mUserTracker.getUserId()) { + if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) { Trace.endSection(); return; } @@ -821,6 +822,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } else if (trustAgentsEnabled && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; + } else if (trustAgentsEnabled + && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) { + return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0 || mUpdateMonitor.isFingerprintLockedOut())) { return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt index 0a55294dfe8a..0b04fb43c2d7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt @@ -46,7 +46,7 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL listeners.forEach(ScreenListener::onScreenTurningOff) } - override fun onScreenTurningOn(ignored: Runnable) { + override fun onScreenTurningOn() { listeners.forEach(ScreenListener::onScreenTurningOn) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java index b3481219a85d..8535eda93f96 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java @@ -18,8 +18,6 @@ package com.android.systemui.keyguard; import android.os.Trace; -import androidx.annotation.NonNull; - import com.android.systemui.Dumpable; import com.android.systemui.dump.DumpManager; @@ -50,14 +48,9 @@ public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> impleme return mScreenState; } - /** - * Dispatch screen turning on events to the registered observers - * - * @param onDrawn Invoke to notify the caller that the event has been processed - */ - public void dispatchScreenTurningOn(@NonNull Runnable onDrawn) { + public void dispatchScreenTurningOn() { setScreenState(SCREEN_TURNING_ON); - dispatch(Observer::onScreenTurningOn, onDrawn); + dispatch(Observer::onScreenTurningOn); } public void dispatchScreenTurnedOn() { @@ -87,12 +80,7 @@ public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> impleme } public interface Observer { - /** - * Receive the screen turning on event - * - * @param onDrawn Invoke to notify the caller that the event has been processed - */ - default void onScreenTurningOn(@NonNull Runnable onDrawn) {} + default void onScreenTurningOn() {} default void onScreenTurnedOn() {} default void onScreenTurningOff() {} default void onScreenTurnedOff() {} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt index 3218f9699f35..5cb7d709a1a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt @@ -22,7 +22,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED +import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.WakefulnessState @@ -50,27 +50,22 @@ constructor( override fun start() { listenForDraggingUpToBouncer() - listenForBouncerHiding() + listenForBouncer() } - private fun listenForBouncerHiding() { + private fun listenForBouncer() { scope.launch { keyguardInteractor.isBouncerShowing .sample( combine( keyguardInteractor.wakefulnessModel, keyguardTransitionInteractor.startedKeyguardTransitionStep, - ) { wakefulnessModel, transitionStep -> - Pair(wakefulnessModel, transitionStep) - } - ) { bouncerShowing, wakefulnessAndTransition -> - Triple( - bouncerShowing, - wakefulnessAndTransition.first, - wakefulnessAndTransition.second - ) - } - .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) -> + ::Pair + ), + ::toTriple + ) + .collect { triple -> + val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple if ( !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER ) { @@ -91,7 +86,19 @@ constructor( animator = getAnimator(), ) ) + } else if ( + isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN + ) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + ownerName = name, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.BOUNCER, + animator = getAnimator(), + ) + ) } + Unit } } } @@ -104,24 +111,20 @@ constructor( combine( keyguardTransitionInteractor.finishedKeyguardState, keyguardInteractor.statusBarState, - ) { finishedKeyguardState, statusBarState -> - Pair(finishedKeyguardState, statusBarState) - } - ) { shadeModel, keyguardStateAndStatusBarState -> - Triple( - shadeModel, - keyguardStateAndStatusBarState.first, - keyguardStateAndStatusBarState.second - ) - } - .collect { (shadeModel, keyguardState, statusBarState) -> + ::Pair + ), + ::toTriple + ) + .collect { triple -> + val (shadeModel, keyguardState, statusBarState) = triple + val id = transitionId if (id != null) { // An existing `id` means a transition is started, and calls to // `updateTransition` will control it until FINISHED keyguardTransitionRepository.updateTransition( id, - shadeModel.expansionAmount, + 1f - shadeModel.expansionAmount, if ( shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f ) { @@ -137,7 +140,7 @@ constructor( if ( keyguardState == KeyguardState.LOCKSCREEN && shadeModel.isUserDragging && - statusBarState != SHADE_LOCKED + statusBarState == KEYGUARD ) { transitionId = keyguardTransitionRepository.startTransition( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index 2cf5fb98d07e..2a3a33eff274 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -77,7 +77,6 @@ constructor( /** Runnable to show the primary bouncer. */ val showRunnable = Runnable { - repository.setPrimaryVisible(true) repository.setPrimaryShow( KeyguardBouncerModel( promptReason = repository.bouncerPromptReason ?: 0, @@ -86,6 +85,7 @@ constructor( ) ) repository.setPrimaryShowingSoon(false) + repository.setPrimaryVisible(true) primaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.VISIBLE) } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index ec2e340762a3..48a68be962dd 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -123,7 +123,7 @@ public class LogModule { @SysUISingleton @QSLog public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) { - return factory.create("QSLog", 500 /* maxSize */, false /* systrace */); + return factory.create("QSLog", 700 /* maxSize */, false /* systrace */); } /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */ diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt index 40ea1e6e87df..11348adb582c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt @@ -35,6 +35,14 @@ enum class ChipStateReceiver( FAR_FROM_SENDER( StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER + ), + TRANSFER_TO_RECEIVER_SUCCEEDED( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_SUCCEEDED, + ), + TRANSFER_TO_RECEIVER_FAILED( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED, + MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED, ); companion object { diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 9ca5fad35ca9..7b9d0b4205af 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -127,8 +127,8 @@ open class MediaTttChipControllerReceiver @Inject constructor( } uiEventLogger.logReceiverStateChange(chipState) - if (chipState == ChipStateReceiver.FAR_FROM_SENDER) { - removeView(routeInfo.id, removalReason = ChipStateReceiver.FAR_FROM_SENDER.name) + if (chipState != ChipStateReceiver.CLOSE_TO_SENDER) { + removeView(routeInfo.id, removalReason = chipState.name) return } if (appIcon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt index 39a276329a9b..6e515f27c25e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt @@ -34,7 +34,11 @@ enum class MediaTttReceiverUiEvents(val metricId: Int) : UiEventLogger.UiEventEn @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_RECEIVER_* docs") MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER(982), @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_RECEIVER_* docs") - MEDIA_TTT_RECEIVER_FAR_FROM_SENDER(983); + MEDIA_TTT_RECEIVER_FAR_FROM_SENDER(983), + @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_RECEIVER_* docs") + MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_SUCCEEDED(1263), + @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_RECEIVER_* docs") + MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED(1264); override fun getId() = metricId } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index e2f55f0d81b8..891455249867 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -464,6 +464,8 @@ public class NavigationBarController implements @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.println("mIsTablet=" + mIsTablet); + pw.println("mNavMode=" + mNavMode); for (int i = 0; i < mNavigationBars.size(); i++) { if (i > 0) { pw.println(); 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 d03ac3b419f6..13c5b48906c5 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -635,8 +635,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private void updateMLModelState() { - boolean newState = mIsEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false); + boolean newState = + mIsGesturalModeEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false); if (newState == mUseMLModel) { return; @@ -766,7 +767,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // ML model boolean withinMinRange = x < mMLEnableWidth + mLeftInset || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset); - if (!withinMinRange && mUseMLModel + if (!withinMinRange && mUseMLModel && !mMLModelIsLoading && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) { withinRange = (results == 1); } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/OWNERS b/packages/SystemUI/src/com/android/systemui/notetask/OWNERS new file mode 100644 index 000000000000..7ccb316dbca5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 1254381 +azappone@google.com +achalke@google.com +juliacr@google.com +madym@google.com +mgalhardo@google.com +petrcermak@google.com +vanjan@google.com
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 8ceee1a950ea..7523d6e976ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -11,7 +11,6 @@ import android.content.Context; import android.content.res.Configuration; import android.os.Bundle; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -31,6 +30,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.QSPanelControllerBase.TileRecord; +import com.android.systemui.qs.logging.QSLogger; import java.util.ArrayList; import java.util.List; @@ -38,11 +38,9 @@ import java.util.Set; public class PagedTileLayout extends ViewPager implements QSTileLayout { - private static final boolean DEBUG = false; private static final String CURRENT_PAGE = "current_page"; private static final int NO_PAGE = -1; - private static final String TAG = "PagedTileLayout"; private static final int REVEAL_SCROLL_DURATION_MILLIS = 750; private static final float BOUNCE_ANIMATION_TENSION = 1.3f; private static final long BOUNCE_ANIMATION_DURATION = 450L; @@ -55,6 +53,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private final ArrayList<TileRecord> mTiles = new ArrayList<>(); private final ArrayList<TileLayout> mPages = new ArrayList<>(); + private QSLogger mLogger; @Nullable private PageIndicator mPageIndicator; private float mPageIndicatorPosition; @@ -146,9 +145,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } if (mLayoutOrientation != newConfig.orientation) { mLayoutOrientation = newConfig.orientation; - mDistributeTiles = true; + forceTilesRedistribution("orientation changed to " + mLayoutOrientation); setCurrentItem(0, false); mPageToRestore = 0; + } else { + // logging in case we missed redistribution because orientation was not changed + // while configuration changed, can be removed after b/255208946 is fixed + mLogger.d( + "Orientation didn't change, tiles might be not redistributed, new config", + newConfig); } } @@ -226,7 +231,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { // Keep on drawing until the animation has finished. postInvalidateOnAnimation(); } catch (NullPointerException e) { - Log.e(TAG, "FakeDragBy called before begin", e); + mLogger.logException("FakeDragBy called before begin", e); // If we were trying to fake drag, it means we just added a new tile to the last // page, so animate there. final int lastPageNumber = mPages.size() - 1; @@ -246,7 +251,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { super.endFakeDrag(); } catch (NullPointerException e) { // Not sure what's going on. Let's log it - Log.e(TAG, "endFakeDrag called without velocityTracker", e); + mLogger.logException("endFakeDrag called without velocityTracker", e); } } @@ -304,14 +309,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void addTile(TileRecord tile) { mTiles.add(tile); - mDistributeTiles = true; + forceTilesRedistribution("adding new tile"); requestLayout(); } @Override public void removeTile(TileRecord tile) { if (mTiles.remove(tile)) { - mDistributeTiles = true; + forceTilesRedistribution("removing tile"); requestLayout(); } } @@ -367,19 +372,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { final int tilesPerPageCount = mPages.get(0).maxTiles(); int index = 0; final int totalTilesCount = mTiles.size(); - if (DEBUG) { - Log.d(TAG, "Distributing tiles: " - + "[tilesPerPageCount=" + tilesPerPageCount + "]" - + "[totalTilesCount=" + totalTilesCount + "]" - ); - } + mLogger.logTileDistributionInProgress(tilesPerPageCount, totalTilesCount); for (int i = 0; i < totalTilesCount; i++) { TileRecord tile = mTiles.get(i); if (mPages.get(index).mRecords.size() == tilesPerPageCount) index++; - if (DEBUG) { - Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " - + index); - } + mLogger.logTileDistributed(tile.tile.getClass().getSimpleName(), index); mPages.get(index).addTile(tile); } } @@ -394,11 +391,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { return; } while (mPages.size() < numPages) { - if (DEBUG) Log.d(TAG, "Adding page"); + mLogger.d("Adding new page"); mPages.add(createTileLayout()); } while (mPages.size() > numPages) { - if (DEBUG) Log.d(TAG, "Removing page"); + mLogger.d("Removing page"); mPages.remove(mPages.size() - 1); } mPageIndicator.setNumPages(mPages.size()); @@ -417,8 +414,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { changed |= mPages.get(i).updateResources(); } if (changed) { - mDistributeTiles = true; + forceTilesRedistribution("resources in pages changed"); requestLayout(); + } else { + // logging in case we missed redistribution because number of column in updateResources + // was not changed, can be removed after b/255208946 is fixed + mLogger.d("resource in pages didn't change, tiles might be not redistributed"); } return changed; } @@ -430,7 +431,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { for (int i = 0; i < mPages.size(); i++) { if (mPages.get(i).setMinRows(minRows)) { changed = true; - mDistributeTiles = true; + forceTilesRedistribution("minRows changed in page"); } } return changed; @@ -443,7 +444,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { for (int i = 0; i < mPages.size(); i++) { if (mPages.get(i).setMaxColumns(maxColumns)) { changed = true; - mDistributeTiles = true; + forceTilesRedistribution("maxColumns in pages changed"); } } return changed; @@ -710,14 +711,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private final PagerAdapter mAdapter = new PagerAdapter() { @Override public void destroyItem(ViewGroup container, int position, Object object) { - if (DEBUG) Log.d(TAG, "Destantiating " + position); + mLogger.d("Destantiating page at", position); container.removeView((View) object); updateListening(); } @Override public Object instantiateItem(ViewGroup container, int position) { - if (DEBUG) Log.d(TAG, "Instantiating " + position); + mLogger.d("Instantiating page at", position); if (isLayoutRtl()) { position = mPages.size() - 1 - position; } @@ -745,10 +746,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { * Force all tiles to be redistributed across pages. * Should be called when one of the following changes: rows, columns, number of tiles. */ - public void forceTilesRedistribution() { + public void forceTilesRedistribution(String reason) { + mLogger.d("forcing tile redistribution across pages, reason", reason); mDistributeTiles = true; } + public void setLogger(QSLogger qsLogger) { + mLogger = qsLogger; + } + public interface PageListener { int INVALID_PAGE = -1; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 6517ff33a49d..7067c220da87 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -43,6 +43,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.widget.RemeasuringLinearLayout; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -106,6 +107,7 @@ public class QSPanel extends LinearLayout implements Tunable { private ViewGroup mMediaHostView; private boolean mShouldMoveMediaOnExpansion = true; private boolean mUsingCombinedHeaders = false; + private QSLogger mQsLogger; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -122,7 +124,8 @@ public class QSPanel extends LinearLayout implements Tunable { } - void initialize() { + void initialize(QSLogger qsLogger) { + mQsLogger = qsLogger; mTileLayout = getOrCreateTileLayout(); if (mUsingMediaPlayer) { @@ -206,6 +209,7 @@ public class QSPanel extends LinearLayout implements Tunable { if (mTileLayout == null) { mTileLayout = (QSTileLayout) LayoutInflater.from(mContext) .inflate(R.layout.qs_paged_tile_layout, this, false); + mTileLayout.setLogger(mQsLogger); mTileLayout.setSquishinessFraction(mSquishinessFraction); } return mTileLayout; @@ -735,6 +739,8 @@ public class QSPanel extends LinearLayout implements Tunable { default void setExpansion(float expansion, float proposedTranslation) {} int getNumVisibleTiles(); + + default void setLogger(QSLogger qsLogger) { } } interface OnConfigurationChangedListener { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index b2ca6b728113..cabe1daf16f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -122,9 +122,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { } switchTileLayout(true); mBrightnessMirrorHandler.onQsPanelAttached(); - - ((PagedTileLayout) mView.getOrCreateTileLayout()) - .setOnTouchListener(mTileLayoutTouchListener); + PagedTileLayout pagedTileLayout= ((PagedTileLayout) mView.getOrCreateTileLayout()); + pagedTileLayout.setOnTouchListener(mTileLayoutTouchListener); } @Override @@ -150,7 +149,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { @Override protected void onSplitShadeChanged() { - ((PagedTileLayout) mView.getOrCreateTileLayout()).forceTilesRedistribution(); + ((PagedTileLayout) mView.getOrCreateTileLayout()) + .forceTilesRedistribution("Split shade state changed"); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 60d2c177c7cd..7bb672ce5880 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -70,7 +70,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr protected final MediaHost mMediaHost; protected final MetricsLogger mMetricsLogger; private final UiEventLogger mUiEventLogger; - private final QSLogger mQSLogger; + protected final QSLogger mQSLogger; private final DumpManager mDumpManager; protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); protected boolean mShouldUseSplitNotificationShade; @@ -152,7 +152,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr @Override protected void onInit() { - mView.initialize(); + mView.initialize(mQSLogger); mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), ""); } @@ -430,6 +430,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr pw.println(" horizontal layout: " + mUsingHorizontalLayout); pw.println(" last orientation: " + mLastOrientation); } + pw.println(" mShouldUseSplitNotificationShade: " + mShouldUseSplitNotificationShade); } public QSPanel.QSTileLayout getTileLayout() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt index 9f6317fd931b..d682853f5fef 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt @@ -21,10 +21,13 @@ import com.android.systemui.log.dagger.QSLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel import com.android.systemui.plugins.log.LogLevel.DEBUG +import com.android.systemui.plugins.log.LogLevel.ERROR import com.android.systemui.plugins.log.LogLevel.VERBOSE +import com.android.systemui.plugins.log.LogLevel.WARNING import com.android.systemui.plugins.log.LogMessage import com.android.systemui.plugins.qs.QSTile import com.android.systemui.statusbar.StatusBarState +import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject private const val TAG = "QSLog" @@ -33,6 +36,26 @@ class QSLogger @Inject constructor( @QSLog private val buffer: LogBuffer ) { + fun d(@CompileTimeConstant msg: String) = buffer.log(TAG, DEBUG, msg) + + fun e(@CompileTimeConstant msg: String) = buffer.log(TAG, ERROR, msg) + + fun v(@CompileTimeConstant msg: String) = buffer.log(TAG, VERBOSE, msg) + + fun w(@CompileTimeConstant msg: String) = buffer.log(TAG, WARNING, msg) + + fun logException(@CompileTimeConstant logMsg: String, ex: Exception) { + buffer.log(TAG, ERROR, {}, { logMsg }, exception = ex) + } + + fun v(@CompileTimeConstant msg: String, arg: Any) { + buffer.log(TAG, VERBOSE, { str1 = arg.toString() }, { "$msg: $str1" }) + } + + fun d(@CompileTimeConstant msg: String, arg: Any) { + buffer.log(TAG, DEBUG, { str1 = arg.toString() }, { "$msg: $str1" }) + } + fun logTileAdded(tileSpec: String) { log(DEBUG, { str1 = tileSpec @@ -236,6 +259,24 @@ class QSLogger @Inject constructor( }) } + fun logTileDistributionInProgress(tilesPerPageCount: Int, totalTilesCount: Int) { + log(DEBUG, { + int1 = tilesPerPageCount + int2 = totalTilesCount + }, { + "Distributing tiles: [tilesPerPageCount=$int1] [totalTilesCount=$int2]" + }) + } + + fun logTileDistributed(tileName: String, pageIndex: Int) { + log(DEBUG, { + str1 = tileName + int1 = pageIndex + }, { + "Adding $str1 to page number $int1" + }) + } + private fun toStateString(state: Int): String { return when (state) { Tile.STATE_ACTIVE -> "active" diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 00d129ae70ca..4d005bebd99e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -889,7 +889,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis * Notifies the Launcher that screen is starting to turn on. */ @Override - public void onScreenTurningOn(@NonNull Runnable ignored) { + public void onScreenTurningOn() { try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurningOn(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java index 959c339ab3e5..f87a1ed57d15 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java @@ -16,14 +16,17 @@ package com.android.systemui.shade; -import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shade.data.repository.ShadeRepository; +import com.android.systemui.shade.data.repository.ShadeRepositoryImpl; import dagger.Binds; import dagger.Module; -/** Provides a {@link ShadeStateEvents} in {@link SysUISingleton} scope. */ +/** Provides Shade-related events and information. */ @Module public abstract class ShadeEventsModule { @Binds abstract ShadeStateEvents bindShadeEvents(ShadeExpansionStateManager impl); + + @Binds abstract ShadeRepository shadeRepository(ShadeRepositoryImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt index 09019a69df47..bf7a2be2e4ca 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt @@ -27,11 +27,17 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +interface ShadeRepository { + /** ShadeModel information regarding shade expansion events */ + val shadeModel: Flow<ShadeModel> +} + /** Business logic for shade interactions */ @SysUISingleton -class ShadeRepository @Inject constructor(shadeExpansionStateManager: ShadeExpansionStateManager) { - - val shadeModel: Flow<ShadeModel> = +class ShadeRepositoryImpl +@Inject +constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepository { + override val shadeModel: Flow<ShadeModel> = conflatedCallbackFlow { val callback = object : ShadeExpansionListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 0f27420e22b0..b7001e476dcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -1193,8 +1193,12 @@ public class KeyguardIndicationController { } @Override - public void onTrustGrantedForCurrentUser(boolean dismissKeyguard, - @NonNull TrustGrantFlags flags, @Nullable String message) { + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + @NonNull TrustGrantFlags flags, + @Nullable String message + ) { showTrustGrantedMessage(dismissKeyguard, message); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt index 42edb309d577..c22dbf615190 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt @@ -27,8 +27,10 @@ class LaunchAnimationParameters( /** * The top position of the notification at the start of the animation. This is needed in order * to keep the notification at its place when launching a notification that is clipped rounded. + * This value is in absolute screen coordinates. */ - var startNotificationTop = 0f + var startNotificationTop = 0 + var notificationParentTop = 0 var startClipTopAmount = 0 var parentStartClipTopAmount = 0 var progress = 0f 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 0d35fdce953e..798bbe8aff7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -92,11 +92,12 @@ class NotificationLaunchAnimatorController( ) params.startTranslationZ = notification.translationZ - params.startNotificationTop = notification.translationY + params.startNotificationTop = location[1] + params.notificationParentTop = notificationListContainer + .getViewParentForNotification(notificationEntry).locationOnScreen[1] params.startRoundedTopClipping = roundedTopClipping params.startClipTopAmount = notification.clipTopAmount if (notification.isChildInGroup) { - params.startNotificationTop += notification.notificationParent.translationY val locationOnScreen = notification.notificationParent.locationOnScreen[1] val parentRoundedClip = (clipStartLocation - locationOnScreen).coerceAtLeast(0) params.parentStartRoundedTopClipping = parentRoundedClip diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 0eb00008e289..dc9b41690d61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -306,9 +306,12 @@ interface Roundable { */ class RoundableState( internal val targetView: View, - roundable: Roundable, - internal val maxRadius: Float, + private val roundable: Roundable, + maxRadius: Float, ) { + internal var maxRadius = maxRadius + private set + /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) @@ -356,6 +359,13 @@ class RoundableState( PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated) } + fun setMaxRadius(radius: Float) { + if (maxRadius != radius) { + maxRadius = radius + roundable.applyRoundnessAndInvalidate() + } + } + fun debugString() = buildString { append("TargetView: ${targetView.hashCode()} ") append("Top: $topRoundness ") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 4133802189a5..76252d0fc5b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -16,8 +16,13 @@ package com.android.systemui.statusbar.notification.collection.coordinator +import android.database.ContentObserver +import android.os.UserHandle +import android.provider.Settings import androidx.annotation.VisibleForTesting +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState @@ -30,11 +35,20 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxy import javax.inject.Inject import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch /** @@ -45,16 +59,19 @@ import kotlinx.coroutines.launch class KeyguardCoordinator @Inject constructor( + @Background private val bgDispatcher: CoroutineDispatcher, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardRepository: KeyguardRepository, private val notifPipelineFlags: NotifPipelineFlags, @Application private val scope: CoroutineScope, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, + private val secureSettings: SecureSettings, private val seenNotifsProvider: SeenNotificationsProviderImpl, private val statusBarStateController: StatusBarStateController, ) : Coordinator { private val unseenNotifications = mutableSetOf<NotificationEntry>() + private var unseenFilterEnabled = false override fun attach(pipeline: NotifPipeline) { setupInvalidateNotifListCallbacks() @@ -71,6 +88,7 @@ constructor( pipeline.addFinalizeFilter(unseenNotifFilter) pipeline.addCollectionListener(collectionListener) scope.launch { clearUnseenWhenKeyguardIsDismissed() } + scope.launch { invalidateWhenUnseenSettingChanges() } } private suspend fun clearUnseenWhenKeyguardIsDismissed() { @@ -85,6 +103,36 @@ constructor( } } + private suspend fun invalidateWhenUnseenSettingChanges() { + secureSettings + // emit whenever the setting has changed + .settingChangesForUser( + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + UserHandle.USER_ALL, + ) + // perform a query immediately + .onStart { emit(Unit) } + // for each change, lookup the new value + .map { + secureSettings.getBoolForUser( + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + UserHandle.USER_CURRENT, + ) + } + // perform lookups on the bg thread pool + .flowOn(bgDispatcher) + // only track the most recent emission, if events are happening faster than they can be + // consumed + .conflate() + // update local field and invalidate if necessary + .collect { setting -> + if (setting != unseenFilterEnabled) { + unseenFilterEnabled = setting + unseenNotifFilter.invalidateList("unseen setting changed") + } + } + } + private val collectionListener = object : NotifCollectionListener { override fun onEntryAdded(entry: NotificationEntry) { @@ -112,6 +160,8 @@ constructor( override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean = when { + // Don't apply filter if the setting is disabled + !unseenFilterEnabled -> false // Don't apply filter if the keyguard isn't currently showing !keyguardRepository.isKeyguardShowing() -> false // Don't apply the filter if the notification is unseen @@ -165,3 +215,15 @@ constructor( private val SEEN_TIMEOUT = 5.seconds } } + +private fun SettingsProxy.settingChangesForUser(name: String, userHandle: Int): Flow<Unit> = + conflatedCallbackFlow { + val observer = + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + trySend(Unit) + } + } + registerContentObserverForUser(name, observer, userHandle) + awaitClose { unregisterContentObserver(observer) } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt new file mode 100644 index 000000000000..b48322822c86 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt @@ -0,0 +1,102 @@ +package com.android.systemui.statusbar.notification.fsi + +import android.app.PendingIntent +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable +import android.os.RemoteException +import android.service.dreams.IDreamManager +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider +import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log +import com.android.systemui.statusbar.phone.CentralSurfaces +import java.util.concurrent.Executor +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +/** + * Class that bridges the gap between clean app architecture and existing code. Provides new + * implementation of StatusBarNotificationActivityStarter launchFullscreenIntent that pipes + * one-directional data => FsiChromeViewModel => FsiChromeView. + */ +@SysUISingleton +class FsiChromeRepo +@Inject +constructor( + private val context: Context, + private val pm: PackageManager, + private val keyguardRepo: KeyguardRepository, + private val launchFullScreenIntentProvider: LaunchFullScreenIntentProvider, + private val featureFlags: FeatureFlags, + private val uiBgExecutor: Executor, + private val dreamManager: IDreamManager, + private val centralSurfaces: CentralSurfaces +) : CoreStartable { + + companion object { + private const val classTag = "FsiChromeRepo" + } + + data class FSIInfo( + val appName: String, + val appIcon: Drawable, + val fullscreenIntent: PendingIntent + ) + + private val _infoFlow = MutableStateFlow<FSIInfo?>(null) + val infoFlow: StateFlow<FSIInfo?> = _infoFlow + + override fun start() { + log("$classTag start listening for FSI notifications") + + // Listen for FSI launch events for the lifetime of SystemUI. + launchFullScreenIntentProvider.registerListener { entry -> launchFullscreenIntent(entry) } + } + + fun dismiss() { + _infoFlow.value = null + } + + fun onFullscreen() { + // TODO(b/243421660) implement transition from container to fullscreen + } + + fun stopScreenSaver() { + uiBgExecutor.execute { + try { + dreamManager.awaken() + } catch (e: RemoteException) { + e.printStackTrace() + } + } + } + + fun launchFullscreenIntent(entry: NotificationEntry) { + if (!featureFlags.isEnabled(Flags.FSI_CHROME)) { + return + } + if (!keyguardRepo.isKeyguardShowing()) { + return + } + stopScreenSaver() + + var appName = pm.getApplicationLabel(context.applicationInfo) as String + val appIcon = pm.getApplicationIcon(context.packageName) + val fullscreenIntent = entry.sbn.notification.fullScreenIntent + + log("FsiChromeRepo launchFullscreenIntent appName=$appName appIcon $appIcon") + _infoFlow.value = FSIInfo(appName, appIcon, fullscreenIntent) + + // If screen is off or we're showing AOD, show lockscreen. + centralSurfaces.wakeUpForFullScreenIntent() + + // Don't show HUN since we're already showing FSI. + entry.notifyFullScreenIntentLaunched() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemory.kt index 0380fff1e2af..1fcf17fe7553 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemory.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemory.kt @@ -17,10 +17,14 @@ package com.android.systemui.statusbar.notification.logging +import android.app.Notification + /** Describes usage of a notification. */ data class NotificationMemoryUsage( val packageName: String, + val uid: Int, val notificationKey: String, + val notification: Notification, val objectUsage: NotificationObjectUsage, val viewUsage: List<NotificationViewUsage> ) @@ -34,7 +38,8 @@ data class NotificationObjectUsage( val smallIcon: Int, val largeIcon: Int, val extras: Int, - val style: String?, + /** Style type, integer from [android.stats.sysui.NotificationEnums] */ + val style: Int, val styleIcon: Int, val bigPicture: Int, val extender: Int, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt new file mode 100644 index 000000000000..ffd931c1bde2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt @@ -0,0 +1,173 @@ +/* + * + * 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.notification.logging + +import android.stats.sysui.NotificationEnums +import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import java.io.PrintWriter +import javax.inject.Inject + +/** Dumps current notification memory use to bug reports for easier debugging. */ +@SysUISingleton +class NotificationMemoryDumper +@Inject +constructor(val dumpManager: DumpManager, val notificationPipeline: NotifPipeline) : Dumpable { + + fun init() { + dumpManager.registerNormalDumpable(javaClass.simpleName, this) + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + val memoryUse = + NotificationMemoryMeter.notificationMemoryUse(notificationPipeline.allNotifs) + .sortedWith(compareBy({ it.packageName }, { it.notificationKey })) + dumpNotificationObjects(pw, memoryUse) + dumpNotificationViewUsage(pw, memoryUse) + } + + /** Renders a table of notification object usage into passed [PrintWriter]. */ + private fun dumpNotificationObjects(pw: PrintWriter, memoryUse: List<NotificationMemoryUsage>) { + pw.println("Notification Object Usage") + pw.println("-----------") + pw.println( + "Package".padEnd(35) + + "\t\tSmall\tLarge\t${"Style".padEnd(15)}\t\tStyle\tBig\tExtend.\tExtras\tCustom" + ) + pw.println("".padEnd(35) + "\t\tIcon\tIcon\t${"".padEnd(15)}\t\tIcon\tPicture\t \t \tView") + pw.println() + + memoryUse.forEach { use -> + pw.println( + use.packageName.padEnd(35) + + "\t\t" + + "${use.objectUsage.smallIcon}\t${use.objectUsage.largeIcon}\t" + + (styleEnumToString(use.objectUsage.style).take(15) ?: "").padEnd(15) + + "\t\t${use.objectUsage.styleIcon}\t" + + "${use.objectUsage.bigPicture}\t${use.objectUsage.extender}\t" + + "${use.objectUsage.extras}\t${use.objectUsage.hasCustomView}\t" + + use.notificationKey + ) + } + + // Calculate totals for easily glanceable summary. + data class Totals( + var smallIcon: Int = 0, + var largeIcon: Int = 0, + var styleIcon: Int = 0, + var bigPicture: Int = 0, + var extender: Int = 0, + var extras: Int = 0, + ) + + val totals = + memoryUse.fold(Totals()) { t, usage -> + t.smallIcon += usage.objectUsage.smallIcon + t.largeIcon += usage.objectUsage.largeIcon + t.styleIcon += usage.objectUsage.styleIcon + t.bigPicture += usage.objectUsage.bigPicture + t.extender += usage.objectUsage.extender + t.extras += usage.objectUsage.extras + t + } + + pw.println() + pw.println("TOTALS") + pw.println( + "".padEnd(35) + + "\t\t" + + "${toKb(totals.smallIcon)}\t${toKb(totals.largeIcon)}\t" + + "".padEnd(15) + + "\t\t${toKb(totals.styleIcon)}\t" + + "${toKb(totals.bigPicture)}\t${toKb(totals.extender)}\t" + + toKb(totals.extras) + ) + pw.println() + } + + /** Renders a table of notification view usage into passed [PrintWriter] */ + private fun dumpNotificationViewUsage( + pw: PrintWriter, + memoryUse: List<NotificationMemoryUsage>, + ) { + + data class Totals( + var smallIcon: Int = 0, + var largeIcon: Int = 0, + var style: Int = 0, + var customViews: Int = 0, + var softwareBitmapsPenalty: Int = 0, + ) + + val totals = Totals() + pw.println("Notification View Usage") + pw.println("-----------") + pw.println("View Type".padEnd(24) + "\tSmall\tLarge\tStyle\tCustom\tSoftware") + pw.println("".padEnd(24) + "\tIcon\tIcon\tUse\tView\tBitmaps") + pw.println() + memoryUse + .filter { it.viewUsage.isNotEmpty() } + .forEach { use -> + pw.println(use.packageName + " " + use.notificationKey) + use.viewUsage.forEach { view -> + pw.println( + " ${view.viewType.toString().padEnd(24)}\t${view.smallIcon}" + + "\t${view.largeIcon}\t${view.style}" + + "\t${view.customViews}\t${view.softwareBitmapsPenalty}" + ) + + if (view.viewType == ViewType.TOTAL) { + totals.smallIcon += view.smallIcon + totals.largeIcon += view.largeIcon + totals.style += view.style + totals.customViews += view.customViews + totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty + } + } + } + pw.println() + pw.println("TOTALS") + pw.println( + " ${"".padEnd(24)}\t${toKb(totals.smallIcon)}" + + "\t${toKb(totals.largeIcon)}\t${toKb(totals.style)}" + + "\t${toKb(totals.customViews)}\t${toKb(totals.softwareBitmapsPenalty)}" + ) + pw.println() + } + + private fun styleEnumToString(styleEnum: Int): String = + when (styleEnum) { + NotificationEnums.STYLE_UNSPECIFIED -> "Unspecified" + NotificationEnums.STYLE_NONE -> "None" + NotificationEnums.STYLE_BIG_PICTURE -> "BigPicture" + NotificationEnums.STYLE_BIG_TEXT -> "BigText" + NotificationEnums.STYLE_CALL -> "Call" + NotificationEnums.STYLE_DECORATED_CUSTOM_VIEW -> "DCustomView" + NotificationEnums.STYLE_INBOX -> "Inbox" + NotificationEnums.STYLE_MEDIA -> "Media" + NotificationEnums.STYLE_MESSAGING -> "Messaging" + NotificationEnums.STYLE_RANKER_GROUP -> "RankerGroup" + else -> "Unknown" + } + + private fun toKb(bytes: Int): String { + return (bytes / 1024).toString() + " KB" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt new file mode 100644 index 000000000000..ec8501a79fa5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt @@ -0,0 +1,194 @@ +/* + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.logging + +import android.app.StatsManager +import android.util.StatsEvent +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.shared.system.SysUiStatsLog +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.util.traceSection +import java.util.concurrent.Executor +import javax.inject.Inject +import kotlin.math.roundToInt +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.runBlocking + +/** Periodically logs current state of notification memory consumption. */ +@SysUISingleton +class NotificationMemoryLogger +@Inject +constructor( + private val notificationPipeline: NotifPipeline, + private val statsManager: StatsManager, + @Main private val mainDispatcher: CoroutineDispatcher, + @Background private val backgroundExecutor: Executor +) : StatsManager.StatsPullAtomCallback { + + /** + * This class is used to accumulate and aggregate data - the fields mirror values in statd Atom + * with ONE IMPORTANT difference - the values are in bytes, not KB! + */ + internal data class NotificationMemoryUseAtomBuilder(val uid: Int, val style: Int) { + var count: Int = 0 + var countWithInflatedViews: Int = 0 + var smallIconObject: Int = 0 + var smallIconBitmapCount: Int = 0 + var largeIconObject: Int = 0 + var largeIconBitmapCount: Int = 0 + var bigPictureObject: Int = 0 + var bigPictureBitmapCount: Int = 0 + var extras: Int = 0 + var extenders: Int = 0 + var smallIconViews: Int = 0 + var largeIconViews: Int = 0 + var systemIconViews: Int = 0 + var styleViews: Int = 0 + var customViews: Int = 0 + var softwareBitmaps: Int = 0 + var seenCount = 0 + } + + fun init() { + statsManager.setPullAtomCallback( + SysUiStatsLog.NOTIFICATION_MEMORY_USE, + null, + backgroundExecutor, + this + ) + } + + /** Called by statsd to pull data. */ + override fun onPullAtom(atomTag: Int, data: MutableList<StatsEvent>): Int = + traceSection("NML#onPullAtom") { + if (atomTag != SysUiStatsLog.NOTIFICATION_MEMORY_USE) { + return StatsManager.PULL_SKIP + } + + // Notifications can only be retrieved on the main thread, so switch to that thread. + val notifications = getAllNotificationsOnMainThread() + val notificationMemoryUse = + NotificationMemoryMeter.notificationMemoryUse(notifications) + .sortedWith( + compareBy( + { it.packageName }, + { it.objectUsage.style }, + { it.notificationKey } + ) + ) + val usageData = aggregateMemoryUsageData(notificationMemoryUse) + usageData.forEach { (_, use) -> + data.add( + SysUiStatsLog.buildStatsEvent( + SysUiStatsLog.NOTIFICATION_MEMORY_USE, + use.uid, + use.style, + use.count, + use.countWithInflatedViews, + toKb(use.smallIconObject), + use.smallIconBitmapCount, + toKb(use.largeIconObject), + use.largeIconBitmapCount, + toKb(use.bigPictureObject), + use.bigPictureBitmapCount, + toKb(use.extras), + toKb(use.extenders), + toKb(use.smallIconViews), + toKb(use.largeIconViews), + toKb(use.systemIconViews), + toKb(use.styleViews), + toKb(use.customViews), + toKb(use.softwareBitmaps), + use.seenCount + ) + ) + } + + return StatsManager.PULL_SUCCESS + } + + private fun getAllNotificationsOnMainThread() = + runBlocking(mainDispatcher) { + traceSection("NML#getNotifications") { notificationPipeline.allNotifs } + } + + /** Aggregates memory usage data by package and style, returning sums. */ + private fun aggregateMemoryUsageData( + notificationMemoryUse: List<NotificationMemoryUsage> + ): Map<Pair<String, Int>, NotificationMemoryUseAtomBuilder> { + return notificationMemoryUse + .groupingBy { Pair(it.packageName, it.objectUsage.style) } + .aggregate { + _, + accumulator: NotificationMemoryUseAtomBuilder?, + element: NotificationMemoryUsage, + first -> + val use = + if (first) { + NotificationMemoryUseAtomBuilder(element.uid, element.objectUsage.style) + } else { + accumulator!! + } + + use.count++ + // If the views of the notification weren't inflated, the list of memory usage + // parameters will be empty. + if (element.viewUsage.isNotEmpty()) { + use.countWithInflatedViews++ + } + + use.smallIconObject += element.objectUsage.smallIcon + if (element.objectUsage.smallIcon > 0) { + use.smallIconBitmapCount++ + } + + use.largeIconObject += element.objectUsage.largeIcon + if (element.objectUsage.largeIcon > 0) { + use.largeIconBitmapCount++ + } + + use.bigPictureObject += element.objectUsage.bigPicture + if (element.objectUsage.bigPicture > 0) { + use.bigPictureBitmapCount++ + } + + use.extras += element.objectUsage.extras + use.extenders += element.objectUsage.extender + + // Use totals count which are more accurate when aggregated + // in this manner. + element.viewUsage + .firstOrNull { vu -> vu.viewType == ViewType.TOTAL } + ?.let { + use.smallIconViews += it.smallIcon + use.largeIconViews += it.largeIcon + use.systemIconViews += it.systemIcons + use.styleViews += it.style + use.customViews += it.style + use.softwareBitmaps += it.softwareBitmapsPenalty + } + + return@aggregate use + } + } + + /** Rounds the passed value to the nearest KB - e.g. 700B rounds to 1KB. */ + private fun toKb(value: Int): Int = (value.toFloat() / 1024f).roundToInt() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeter.kt index 7d39e18ab349..41fb91e3093e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeter.kt @@ -1,12 +1,20 @@ package com.android.systemui.statusbar.notification.logging import android.app.Notification +import android.app.Notification.BigPictureStyle +import android.app.Notification.BigTextStyle +import android.app.Notification.CallStyle +import android.app.Notification.DecoratedCustomViewStyle +import android.app.Notification.InboxStyle +import android.app.Notification.MediaStyle +import android.app.Notification.MessagingStyle import android.app.Person import android.graphics.Bitmap import android.graphics.drawable.Icon import android.os.Bundle import android.os.Parcel import android.os.Parcelable +import android.stats.sysui.NotificationEnums import androidx.annotation.WorkerThread import com.android.systemui.statusbar.notification.NotificationUtils import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -19,6 +27,7 @@ internal object NotificationMemoryMeter { private const val TV_EXTENSIONS = "android.tv.EXTENSIONS" private const val WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS" private const val WEARABLE_EXTENSIONS_BACKGROUND = "background" + private const val AUTOGROUP_KEY = "ranker_group" /** Returns a list of memory use entries for currently shown notifications. */ @WorkerThread @@ -29,12 +38,15 @@ internal object NotificationMemoryMeter { .asSequence() .map { entry -> val packageName = entry.sbn.packageName + val uid = entry.sbn.uid val notificationObjectUsage = notificationMemoryUse(entry.sbn.notification, hashSetOf()) val notificationViewUsage = NotificationMemoryViewWalker.getViewUsage(entry.row) NotificationMemoryUsage( packageName, + uid, NotificationUtils.logKey(entry.sbn.key), + entry.sbn.notification, notificationObjectUsage, notificationViewUsage ) @@ -49,7 +61,9 @@ internal object NotificationMemoryMeter { ): NotificationMemoryUsage { return NotificationMemoryUsage( entry.sbn.packageName, + entry.sbn.uid, NotificationUtils.logKey(entry.sbn.key), + entry.sbn.notification, notificationMemoryUse(entry.sbn.notification, seenBitmaps), NotificationMemoryViewWalker.getViewUsage(entry.row) ) @@ -116,7 +130,13 @@ internal object NotificationMemoryMeter { val wearExtenderBackground = computeParcelableUse(wearExtender, WEARABLE_EXTENSIONS_BACKGROUND, seenBitmaps) - val style = notification.notificationStyle + val style = + if (notification.group == AUTOGROUP_KEY) { + NotificationEnums.STYLE_RANKER_GROUP + } else { + styleEnum(notification.notificationStyle) + } + val hasCustomView = notification.contentView != null || notification.bigContentView != null val extrasSize = computeBundleSize(extras) @@ -124,7 +144,7 @@ internal object NotificationMemoryMeter { smallIcon = smallIconUse, largeIcon = largeIconUse, extras = extrasSize, - style = style?.simpleName, + style = style, styleIcon = bigPictureIconUse + peopleUse + @@ -144,6 +164,25 @@ internal object NotificationMemoryMeter { } /** + * Returns logging style enum based on current style class. + * + * @return style value in [NotificationEnums] + */ + private fun styleEnum(style: Class<out Notification.Style>?): Int = + when (style?.name) { + null -> NotificationEnums.STYLE_NONE + BigTextStyle::class.java.name -> NotificationEnums.STYLE_BIG_TEXT + BigPictureStyle::class.java.name -> NotificationEnums.STYLE_BIG_PICTURE + InboxStyle::class.java.name -> NotificationEnums.STYLE_INBOX + MediaStyle::class.java.name -> NotificationEnums.STYLE_MEDIA + DecoratedCustomViewStyle::class.java.name -> + NotificationEnums.STYLE_DECORATED_CUSTOM_VIEW + MessagingStyle::class.java.name -> NotificationEnums.STYLE_MESSAGING + CallStyle::class.java.name -> NotificationEnums.STYLE_CALL + else -> NotificationEnums.STYLE_UNSPECIFIED + } + + /** * Calculates size of the bundle data (excluding FDs and other shared objects like ashmem * bitmaps). Can be slow. */ @@ -176,7 +215,7 @@ internal object NotificationMemoryMeter { * * @return memory usage in bytes or 0 if the icon is Uri/Resource based */ - private fun computeIconUse(icon: Icon?, seenBitmaps: HashSet<Int>) = + private fun computeIconUse(icon: Icon?, seenBitmaps: HashSet<Int>): Int = when (icon?.type) { Icon.TYPE_BITMAP -> computeBitmapUse(icon.bitmap, seenBitmaps) Icon.TYPE_ADAPTIVE_BITMAP -> computeBitmapUse(icon.bitmap, seenBitmaps) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt index c09cc4306ced..f38c1e557b25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt @@ -18,11 +18,10 @@ package com.android.systemui.statusbar.notification.logging import android.util.Log -import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dump.DumpManager -import com.android.systemui.statusbar.notification.collection.NotifPipeline -import java.io.PrintWriter +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import dagger.Lazy import javax.inject.Inject /** This class monitors and logs current Notification memory use. */ @@ -30,9 +29,10 @@ import javax.inject.Inject class NotificationMemoryMonitor @Inject constructor( - val notificationPipeline: NotifPipeline, - val dumpManager: DumpManager, -) : Dumpable { + private val featureFlags: FeatureFlags, + private val notificationMemoryDumper: NotificationMemoryDumper, + private val notificationMemoryLogger: Lazy<NotificationMemoryLogger>, +) { companion object { private const val TAG = "NotificationMemory" @@ -40,127 +40,10 @@ constructor( fun init() { Log.d(TAG, "NotificationMemoryMonitor initialized.") - dumpManager.registerDumpable(javaClass.simpleName, this) - } - - override fun dump(pw: PrintWriter, args: Array<out String>) { - val memoryUse = - NotificationMemoryMeter.notificationMemoryUse(notificationPipeline.allNotifs) - .sortedWith(compareBy({ it.packageName }, { it.notificationKey })) - dumpNotificationObjects(pw, memoryUse) - dumpNotificationViewUsage(pw, memoryUse) - } - - /** Renders a table of notification object usage into passed [PrintWriter]. */ - private fun dumpNotificationObjects(pw: PrintWriter, memoryUse: List<NotificationMemoryUsage>) { - pw.println("Notification Object Usage") - pw.println("-----------") - pw.println( - "Package".padEnd(35) + - "\t\tSmall\tLarge\t${"Style".padEnd(15)}\t\tStyle\tBig\tExtend.\tExtras\tCustom" - ) - pw.println("".padEnd(35) + "\t\tIcon\tIcon\t${"".padEnd(15)}\t\tIcon\tPicture\t \t \tView") - pw.println() - - memoryUse.forEach { use -> - pw.println( - use.packageName.padEnd(35) + - "\t\t" + - "${use.objectUsage.smallIcon}\t${use.objectUsage.largeIcon}\t" + - (use.objectUsage.style?.take(15) ?: "").padEnd(15) + - "\t\t${use.objectUsage.styleIcon}\t" + - "${use.objectUsage.bigPicture}\t${use.objectUsage.extender}\t" + - "${use.objectUsage.extras}\t${use.objectUsage.hasCustomView}\t" + - use.notificationKey - ) + notificationMemoryDumper.init() + if (featureFlags.isEnabled(Flags.NOTIFICATION_MEMORY_LOGGING_ENABLED)) { + Log.d(TAG, "Notification memory logging enabled.") + notificationMemoryLogger.get().init() } - - // Calculate totals for easily glanceable summary. - data class Totals( - var smallIcon: Int = 0, - var largeIcon: Int = 0, - var styleIcon: Int = 0, - var bigPicture: Int = 0, - var extender: Int = 0, - var extras: Int = 0, - ) - - val totals = - memoryUse.fold(Totals()) { t, usage -> - t.smallIcon += usage.objectUsage.smallIcon - t.largeIcon += usage.objectUsage.largeIcon - t.styleIcon += usage.objectUsage.styleIcon - t.bigPicture += usage.objectUsage.bigPicture - t.extender += usage.objectUsage.extender - t.extras += usage.objectUsage.extras - t - } - - pw.println() - pw.println("TOTALS") - pw.println( - "".padEnd(35) + - "\t\t" + - "${toKb(totals.smallIcon)}\t${toKb(totals.largeIcon)}\t" + - "".padEnd(15) + - "\t\t${toKb(totals.styleIcon)}\t" + - "${toKb(totals.bigPicture)}\t${toKb(totals.extender)}\t" + - toKb(totals.extras) - ) - pw.println() - } - - /** Renders a table of notification view usage into passed [PrintWriter] */ - private fun dumpNotificationViewUsage( - pw: PrintWriter, - memoryUse: List<NotificationMemoryUsage>, - ) { - - data class Totals( - var smallIcon: Int = 0, - var largeIcon: Int = 0, - var style: Int = 0, - var customViews: Int = 0, - var softwareBitmapsPenalty: Int = 0, - ) - - val totals = Totals() - pw.println("Notification View Usage") - pw.println("-----------") - pw.println("View Type".padEnd(24) + "\tSmall\tLarge\tStyle\tCustom\tSoftware") - pw.println("".padEnd(24) + "\tIcon\tIcon\tUse\tView\tBitmaps") - pw.println() - memoryUse - .filter { it.viewUsage.isNotEmpty() } - .forEach { use -> - pw.println(use.packageName + " " + use.notificationKey) - use.viewUsage.forEach { view -> - pw.println( - " ${view.viewType.toString().padEnd(24)}\t${view.smallIcon}" + - "\t${view.largeIcon}\t${view.style}" + - "\t${view.customViews}\t${view.softwareBitmapsPenalty}" - ) - - if (view.viewType == ViewType.TOTAL) { - totals.smallIcon += view.smallIcon - totals.largeIcon += view.largeIcon - totals.style += view.style - totals.customViews += view.customViews - totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty - } - } - } - pw.println() - pw.println("TOTALS") - pw.println( - " ${"".padEnd(24)}\t${toKb(totals.smallIcon)}" + - "\t${toKb(totals.largeIcon)}\t${toKb(totals.style)}" + - "\t${toKb(totals.customViews)}\t${toKb(totals.softwareBitmapsPenalty)}" - ) - pw.println() - } - - private fun toKb(bytes: Int): String { - return (bytes / 1024).toString() + " KB" } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt index a0bee1502f51..2d042118b3d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt @@ -50,7 +50,11 @@ internal object NotificationMemoryViewWalker { /** * Returns memory usage of public and private views contained in passed - * [ExpandableNotificationRow] + * [ExpandableNotificationRow]. Each entry will correspond to one of the [ViewType] values with + * [ViewType.TOTAL] totalling all memory use. If a type of view is missing, the corresponding + * entry will not appear in resulting list. + * + * This will return an empty list if the ExpandableNotificationRow has no views inflated. */ fun getViewUsage(row: ExpandableNotificationRow?): List<NotificationViewUsage> { if (row == null) { @@ -58,42 +62,72 @@ internal object NotificationMemoryViewWalker { } // The ordering here is significant since it determines deduplication of seen drawables. - return listOf( - getViewUsage(ViewType.PRIVATE_EXPANDED_VIEW, row.privateLayout?.expandedChild), - getViewUsage(ViewType.PRIVATE_CONTRACTED_VIEW, row.privateLayout?.contractedChild), - getViewUsage(ViewType.PRIVATE_HEADS_UP_VIEW, row.privateLayout?.headsUpChild), - getViewUsage(ViewType.PUBLIC_VIEW, row.publicLayout), - getTotalUsage(row) - ) + val perViewUsages = + listOf( + getViewUsage(ViewType.PRIVATE_EXPANDED_VIEW, row.privateLayout?.expandedChild), + getViewUsage( + ViewType.PRIVATE_CONTRACTED_VIEW, + row.privateLayout?.contractedChild + ), + getViewUsage(ViewType.PRIVATE_HEADS_UP_VIEW, row.privateLayout?.headsUpChild), + getViewUsage( + ViewType.PUBLIC_VIEW, + row.publicLayout?.expandedChild, + row.publicLayout?.contractedChild, + row.publicLayout?.headsUpChild + ), + ) + .filterNotNull() + + return if (perViewUsages.isNotEmpty()) { + // Attach summed totals field only if there was any view actually measured. + // This reduces bug report noise and makes checks for collapsed views easier. + val totals = getTotalUsage(row) + if (totals == null) { + perViewUsages + } else { + perViewUsages + totals + } + } else { + listOf() + } } /** * Calculate total usage of all views - we need to do a separate traversal to make sure we don't * double count fields. */ - private fun getTotalUsage(row: ExpandableNotificationRow): NotificationViewUsage { - val totalUsage = UsageBuilder() + private fun getTotalUsage(row: ExpandableNotificationRow): NotificationViewUsage? { val seenObjects = hashSetOf<Int>() - - row.publicLayout?.let { computeViewHierarchyUse(it, totalUsage, seenObjects) } - row.privateLayout?.let { child -> - for (view in listOf(child.expandedChild, child.contractedChild, child.headsUpChild)) { - (view as? ViewGroup)?.let { v -> - computeViewHierarchyUse(v, totalUsage, seenObjects) - } - } - } - return totalUsage.build(ViewType.TOTAL) + return getViewUsage( + ViewType.TOTAL, + row.privateLayout?.expandedChild, + row.privateLayout?.contractedChild, + row.privateLayout?.headsUpChild, + row.publicLayout?.expandedChild, + row.publicLayout?.contractedChild, + row.publicLayout?.headsUpChild, + seenObjects = seenObjects + ) } private fun getViewUsage( type: ViewType, - rootView: View?, + vararg rootViews: View?, seenObjects: HashSet<Int> = hashSetOf() - ): NotificationViewUsage { - val usageBuilder = UsageBuilder() - (rootView as? ViewGroup)?.let { computeViewHierarchyUse(it, usageBuilder, seenObjects) } - return usageBuilder.build(type) + ): NotificationViewUsage? { + val usageBuilder = lazy { UsageBuilder() } + rootViews.forEach { rootView -> + (rootView as? ViewGroup)?.let { rootViewGroup -> + computeViewHierarchyUse(rootViewGroup, usageBuilder.value, seenObjects) + } + } + + return if (usageBuilder.isInitialized()) { + usageBuilder.value.build(type) + } else { + null + } } private fun computeViewHierarchyUse( 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 c7c1634ea105..44a231d55c08 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 @@ -976,10 +976,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * Updates states of all children. */ - public void updateChildrenStates(AmbientState ambientState) { + public void updateChildrenStates() { if (mIsSummaryWithChildren) { ExpandableViewState parentState = getViewState(); - mChildrenContainer.updateState(parentState, ambientState); + mChildrenContainer.updateState(parentState); } } @@ -2223,6 +2223,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mNotificationParent != null) { mNotificationParent.setClipTopAmount(0); } + setTranslationX(0); return; } @@ -2241,6 +2242,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setTranslationZ(translationZ); float extraWidthForClipping = params.getWidth() - getWidth(); setExtraWidthForClipping(extraWidthForClipping); + int top; if (params.getStartRoundedTopClipping() > 0) { // If we were clipping initially, let's interpolate from the start position to the @@ -2248,20 +2250,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( params.getProgress(0, NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); - float startTop = params.getStartNotificationTop(); - top = (int) Math.min(MathUtils.lerp(startTop, - params.getTop(), expandProgress), + int startTop = params.getStartNotificationTop(); + top = (int) Math.min(MathUtils.lerp(startTop, params.getTop(), expandProgress), startTop); } else { top = params.getTop(); } int actualHeight = params.getBottom() - top; setActualHeight(actualHeight); + + int notificationStackTop = params.getNotificationParentTop(); + top -= notificationStackTop; int startClipTopAmount = params.getStartClipTopAmount(); int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress()); if (mNotificationParent != null) { - float parentY = mNotificationParent.getTranslationY(); - top -= parentY; + float parentTranslationY = mNotificationParent.getTranslationY(); + top -= parentTranslationY; mNotificationParent.setTranslationZ(translationZ); // When the expanding notification is below its parent, the parent must be clipped @@ -2270,15 +2274,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // pixels to show the expanding notification, while still taking the decreasing // notification clipTopAmount into consideration, so 'top + clipTopAmount'. int parentStartClipTopAmount = params.getParentStartClipTopAmount(); - int parentClipTopAmount = Math.min(parentStartClipTopAmount, - top + clipTopAmount); + int parentClipTopAmount = Math.min(parentStartClipTopAmount, top + clipTopAmount); mNotificationParent.setClipTopAmount(parentClipTopAmount); mNotificationParent.setExtraWidthForClipping(extraWidthForClipping); - float clipBottom = Math.max(params.getBottom(), - parentY + mNotificationParent.getActualHeight() + float clipBottom = Math.max(params.getBottom() - notificationStackTop, + parentTranslationY + mNotificationParent.getActualHeight() - mNotificationParent.getClipBottomAmount()); - float clipTop = Math.min(params.getTop(), parentY); + float clipTop = Math.min(params.getTop() - notificationStackTop, parentTranslationY); int minimumHeightForClipping = (int) (clipBottom - clipTop); mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping); } else if (startClipTopAmount != 0) { @@ -2286,6 +2289,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } setTranslationY(top); + float absoluteCenterX = getLocationOnScreen()[0] + getWidth() / 2f - getTranslationX(); + setTranslationX(params.getCenterX() - absoluteCenterX); + final float maxRadius = getMaxRadius(); mTopRoundnessDuringLaunchAnimation = params.getTopCornerRadius() / maxRadius; mBottomRoundnessDuringLaunchAnimation = params.getBottomCornerRadius() / maxRadius; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 0213b969551e..20412452b7f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -214,7 +214,11 @@ public abstract class ExpandableOutlineView extends ExpandableView { } else { maxRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius); } - mRoundableState = new RoundableState(this, this, maxRadius); + if (mRoundableState == null) { + mRoundableState = new RoundableState(this, this, maxRadius); + } else { + mRoundableState.setMaxRadius(maxRadius); + } setClipToOutline(mAlwaysRoundBothCorners); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 4a8e2dbb5334..4d1451e3a6e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -616,9 +616,8 @@ public class NotificationChildrenContainer extends ViewGroup * Update the state of all its children based on a linear layout algorithm. * * @param parentState the state of the parent - * @param ambientState the ambient state containing ambient information */ - public void updateState(ExpandableViewState parentState, AmbientState ambientState) { + public void updateState(ExpandableViewState parentState) { int childCount = mAttachedChildren.size(); int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation; boolean firstChild = true; @@ -661,9 +660,17 @@ public class NotificationChildrenContainer extends ViewGroup childState.height = intrinsicHeight; childState.setYTranslation(yPosition + launchTransitionCompensation); childState.hidden = false; - // When the group is expanded, the children cast the shadows rather than the parent - // so use the parent's elevation here. - if (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications) { + if (child.isExpandAnimationRunning() || mContainingNotification.hasExpandingChild()) { + // Not modifying translationZ during launch animation. The translationZ of the + // expanding child is handled inside ExpandableNotificationRow and the translationZ + // of the other children inside the group should remain unchanged. In particular, + // they should not take over the translationZ of the parent, since the parent has + // a positive translationZ set only for the expanding child to be drawn above other + // notifications. + childState.setZTranslation(child.getTranslationZ()); + } else if (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications) { + // When the group is expanded, the children cast the shadows rather than the parent + // so use the parent's elevation here. childState.setZTranslation(parentState.getZTranslation()); } else { childState.setZTranslation(0); @@ -716,9 +723,15 @@ public class NotificationChildrenContainer extends ViewGroup mHeaderViewState = new ViewState(); } mHeaderViewState.initFrom(mNotificationHeader); - mHeaderViewState.setZTranslation(childrenExpandedAndNotAnimating - ? parentState.getZTranslation() - : 0); + + if (mContainingNotification.hasExpandingChild()) { + // Not modifying translationZ during expand animation. + mHeaderViewState.setZTranslation(mNotificationHeader.getTranslationZ()); + } else if (childrenExpandedAndNotAnimating) { + mHeaderViewState.setZTranslation(parentState.getZTranslation()); + } else { + mHeaderViewState.setZTranslation(0); + } mHeaderViewState.setYTranslation(mCurrentHeaderTranslation); mHeaderViewState.setAlpha(mHeaderVisibleAmount); // The hiding is done automatically by the alpha, otherwise we'll pick it up again 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 21e2bd877bae..d22bfe8b9e3c 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 @@ -5772,14 +5772,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable || mExpandingNotificationRow == null) { return; } - int left = Math.min(mLaunchAnimationParams.getLeft(), mRoundedRectClippingLeft); - int right = Math.max(mLaunchAnimationParams.getRight(), mRoundedRectClippingRight); - int bottom = Math.max(mLaunchAnimationParams.getBottom(), mRoundedRectClippingBottom); + int[] absoluteCoords = new int[2]; + getLocationOnScreen(absoluteCoords); + + int left = Math.min(mLaunchAnimationParams.getLeft() - absoluteCoords[0], + mRoundedRectClippingLeft); + int right = Math.max(mLaunchAnimationParams.getRight() - absoluteCoords[0], + mRoundedRectClippingRight); + int bottom = Math.max(mLaunchAnimationParams.getBottom() - absoluteCoords[1], + mRoundedRectClippingBottom); float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( mLaunchAnimationParams.getProgress(0, NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop, - mLaunchAnimationParams.getTop(), expandProgress), + mLaunchAnimationParams.getTop() - absoluteCoords[1], expandProgress), mRoundedRectClippingTop); float topRadius = mLaunchAnimationParams.getTopCornerRadius(); float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index b6cf9482f00d..6e6396047361 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -128,7 +128,7 @@ public class StackScrollAlgorithm { updateSpeedBumpState(algorithmState, speedBumpIndex); updateShelfState(algorithmState, ambientState); updateAlphaState(algorithmState, ambientState); - getNotificationChildrenStates(algorithmState, ambientState); + getNotificationChildrenStates(algorithmState); } private void updateAlphaState(StackScrollAlgorithmState algorithmState, @@ -231,14 +231,13 @@ public class StackScrollAlgorithm { } } - private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState) { int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { ExpandableView v = algorithmState.visibleChildren.get(i); if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; - row.updateChildrenStates(ambientState); + row.updateChildrenStates(); } } } 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 005cd1bff90c..93da0b806a78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -3574,7 +3574,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override - public void onScreenTurningOn(Runnable onDrawn) { + public void onScreenTurningOn() { mFalsingCollector.onScreenTurningOn(); mNotificationPanelViewController.onScreenTurningOn(); } 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 05bf8604c2c0..be6e0cc8a996 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -50,6 +50,8 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.ShadeController; @@ -106,6 +108,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final LockPatternUtils mLockPatternUtils; private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback; private final ActivityIntentHelper mActivityIntentHelper; + private final FeatureFlags mFeatureFlags; private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; @@ -149,7 +152,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte NotificationPanelViewController panel, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, - LaunchFullScreenIntentProvider launchFullScreenIntentProvider) { + LaunchFullScreenIntentProvider launchFullScreenIntentProvider, + FeatureFlags featureFlags) { mContext = context; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; @@ -170,6 +174,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLockPatternUtils = lockPatternUtils; mStatusBarRemoteInputCallback = remoteInputCallback; mActivityIntentHelper = activityIntentHelper; + mFeatureFlags = featureFlags; mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; @@ -548,7 +553,10 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLogger.logFullScreenIntentSuppressedByVR(entry); return; } - + if (mFeatureFlags.isEnabled(Flags.FSI_CHROME)) { + // FsiChromeRepo runs its own implementation of launchFullScreenIntent + return; + } // Stop screensaver if the notification has a fullscreen intent. // (like an incoming phone call) mUiBgExecutor.execute(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/stylus/OWNERS b/packages/SystemUI/src/com/android/systemui/stylus/OWNERS new file mode 100644 index 000000000000..7ccb316dbca5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stylus/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 1254381 +azappone@google.com +achalke@google.com +juliacr@google.com +madym@google.com +mgalhardo@google.com +petrcermak@google.com +vanjan@google.com
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt new file mode 100644 index 000000000000..11233dda165c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt @@ -0,0 +1,118 @@ +/* + * 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.stylus + +import android.hardware.BatteryState +import android.hardware.input.InputManager +import android.util.Log +import android.view.InputDevice +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import java.util.concurrent.Executor +import javax.inject.Inject + +/** + * A [CoreStartable] that listens to USI stylus battery events, to manage the [StylusUsiPowerUI] + * notification controller. + */ +@SysUISingleton +class StylusUsiPowerStartable +@Inject +constructor( + private val stylusManager: StylusManager, + private val inputManager: InputManager, + private val stylusUsiPowerUi: StylusUsiPowerUI, + private val featureFlags: FeatureFlags, + @Background private val executor: Executor, +) : CoreStartable, StylusManager.StylusCallback, InputManager.InputDeviceBatteryListener { + + override fun onStylusAdded(deviceId: Int) { + val device = inputManager.getInputDevice(deviceId) ?: return + + if (!device.isExternal) { + registerBatteryListener(deviceId) + } + } + + override fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) { + stylusUsiPowerUi.refresh() + } + + override fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) { + stylusUsiPowerUi.refresh() + } + + override fun onStylusRemoved(deviceId: Int) { + val device = inputManager.getInputDevice(deviceId) ?: return + + if (!device.isExternal) { + unregisterBatteryListener(deviceId) + } + } + + override fun onBatteryStateChanged( + deviceId: Int, + eventTimeMillis: Long, + batteryState: BatteryState + ) { + if (batteryState.isPresent) { + stylusUsiPowerUi.updateBatteryState(batteryState) + } + } + + private fun registerBatteryListener(deviceId: Int) { + try { + inputManager.addInputDeviceBatteryListener(deviceId, executor, this) + } catch (e: SecurityException) { + Log.e(TAG, "$e: Failed to register battery listener for $deviceId.") + } + } + + private fun unregisterBatteryListener(deviceId: Int) { + try { + inputManager.removeInputDeviceBatteryListener(deviceId, this) + } catch (e: SecurityException) { + Log.e(TAG, "$e: Failed to unregister battery listener for $deviceId.") + } + } + + override fun start() { + if (!featureFlags.isEnabled(Flags.ENABLE_USI_BATTERY_NOTIFICATIONS)) return + addBatteryListenerForInternalStyluses() + + stylusManager.registerCallback(this) + stylusManager.startListener() + } + + private fun addBatteryListenerForInternalStyluses() { + // For most devices, an active stylus is represented by an internal InputDevice. + // This InputDevice will be present in InputManager before CoreStartables run, + // and will not be removed. In many cases, it reports the battery level of the stylus. + inputManager.inputDeviceIds + .asSequence() + .mapNotNull { inputManager.getInputDevice(it) } + .filter { it.supportsSource(InputDevice.SOURCE_STYLUS) } + .forEach { onStylusAdded(it.id) } + } + + companion object { + private val TAG = StylusUsiPowerStartable::class.simpleName.orEmpty() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt new file mode 100644 index 000000000000..70a5b366263e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -0,0 +1,185 @@ +/* + * 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.stylus + +import android.Manifest +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.hardware.BatteryState +import android.hardware.input.InputManager +import android.os.Handler +import android.os.UserHandle +import android.view.InputDevice +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.NotificationChannels +import java.text.NumberFormat +import javax.inject.Inject + +/** + * UI controller for the notification that shows when a USI stylus battery is low. The + * [StylusUsiPowerStartable], which listens to battery events, uses this controller. + */ +@SysUISingleton +class StylusUsiPowerUI +@Inject +constructor( + private val context: Context, + private val notificationManager: NotificationManagerCompat, + private val inputManager: InputManager, + @Background private val handler: Handler, +) { + + // These values must only be accessed on the handler. + private var batteryCapacity = 1.0f + private var suppressed = false + + fun init() { + val filter = + IntentFilter().also { + it.addAction(ACTION_DISMISSED_LOW_BATTERY) + it.addAction(ACTION_CLICKED_LOW_BATTERY) + } + + context.registerReceiverAsUser( + receiver, + UserHandle.ALL, + filter, + Manifest.permission.DEVICE_POWER, + handler, + Context.RECEIVER_NOT_EXPORTED, + ) + } + + fun refresh() { + handler.post refreshNotification@{ + if (!suppressed && !hasConnectedBluetoothStylus() && isBatteryBelowThreshold()) { + showOrUpdateNotification() + return@refreshNotification + } + + if (!isBatteryBelowThreshold()) { + // Reset suppression when stylus battery is recharged, so that the next time + // it reaches a low battery, the notification will show again. + suppressed = false + } + hideNotification() + } + } + + fun updateBatteryState(batteryState: BatteryState) { + handler.post updateBattery@{ + if (batteryState.capacity == batteryCapacity) return@updateBattery + + batteryCapacity = batteryState.capacity + refresh() + } + } + + /** + * Suppression happens when the notification is dismissed by the user. This is to prevent + * further battery events with capacities below the threshold from reopening the suppressed + * notification. + * + * Suppression can only be removed when the battery has been recharged - thus restarting the + * notification cycle (i.e. next low battery event, notification should show). + */ + fun updateSuppression(suppress: Boolean) { + handler.post updateSuppressed@{ + if (suppressed == suppress) return@updateSuppressed + + suppressed = suppress + refresh() + } + } + + private fun hideNotification() { + notificationManager.cancel(USI_NOTIFICATION_ID) + } + + private fun showOrUpdateNotification() { + val notification = + NotificationCompat.Builder(context, NotificationChannels.BATTERY) + .setSmallIcon(R.drawable.ic_power_low) + .setDeleteIntent(getPendingBroadcast(ACTION_DISMISSED_LOW_BATTERY)) + .setContentIntent(getPendingBroadcast(ACTION_CLICKED_LOW_BATTERY)) + .setContentTitle(context.getString(R.string.stylus_battery_low)) + .setContentText( + context.getString( + R.string.battery_low_percent_format, + NumberFormat.getPercentInstance().format(batteryCapacity) + ) + ) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setLocalOnly(true) + .setAutoCancel(true) + .build() + + notificationManager.notify(USI_NOTIFICATION_ID, notification) + } + + private fun isBatteryBelowThreshold(): Boolean { + return batteryCapacity <= LOW_BATTERY_THRESHOLD + } + + private fun hasConnectedBluetoothStylus(): Boolean { + // TODO(b/257936830): get bt address once input api available + return inputManager.inputDeviceIds.any { deviceId -> + inputManager.getInputDevice(deviceId).supportsSource(InputDevice.SOURCE_STYLUS) + } + } + + private fun getPendingBroadcast(action: String): PendingIntent? { + return PendingIntent.getBroadcastAsUser( + context, + 0, + Intent(action), + PendingIntent.FLAG_IMMUTABLE, + UserHandle.CURRENT + ) + } + + private val receiver: BroadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + ACTION_DISMISSED_LOW_BATTERY -> updateSuppression(true) + ACTION_CLICKED_LOW_BATTERY -> { + updateSuppression(true) + // TODO(b/261584943): open USI device details page + } + } + } + } + + companion object { + // Low battery threshold matches CrOS, see: + // https://source.chromium.org/chromium/chromium/src/+/main:ash/system/power/peripheral_battery_notifier.cc;l=41 + private const val LOW_BATTERY_THRESHOLD = 0.16f + + private val USI_NOTIFICATION_ID = R.string.stylus_battery_low + + private const val ACTION_DISMISSED_LOW_BATTERY = "StylusUsiPowerUI.dismiss" + private const val ACTION_CLICKED_LOW_BATTERY = "StylusUsiPowerUI.click" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt index 14ba63a2738f..f37ef828ebdd 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt @@ -22,10 +22,13 @@ import android.os.PowerManager import android.view.Gravity import android.view.MotionEvent import android.view.View +import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE +import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE import android.view.ViewGroup import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.widget.TextView +import androidx.annotation.IdRes import com.android.internal.widget.CachingIconView import com.android.systemui.Gefingerpoken import com.android.systemui.R @@ -113,6 +116,8 @@ open class ChipbarCoordinator @Inject constructor( } ) + currentView.setTag(INFO_TAG, newInfo) + // Detect falsing touches on the chip. parent = currentView.requireViewById(R.id.chipbar_root_view) parent.touchHandler = object : Gefingerpoken { @@ -165,8 +170,11 @@ open class ChipbarCoordinator @Inject constructor( } else { "" } - currentView.requireViewById<ViewGroup>(R.id.chipbar_inner).contentDescription = - "$loadedIconDesc${newInfo.text.loadText(context)}" + + val chipInnerView = currentView.getInnerView() + chipInnerView.contentDescription = "$loadedIconDesc${newInfo.text.loadText(context)}" + chipInnerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE + maybeGetAccessibilityFocus(newInfo, currentView) // ---- Haptics ---- newInfo.vibrationEffect?.let { @@ -174,23 +182,37 @@ open class ChipbarCoordinator @Inject constructor( } } + private fun maybeGetAccessibilityFocus(info: ChipbarInfo?, view: ViewGroup) { + // Don't steal focus unless the chipbar has something interactable. + // (The chipbar is marked as a live region, so its content will be announced whenever the + // content changes.) + if (info?.endItem is ChipbarEndItem.Button) { + view.getInnerView().requestAccessibilityFocus() + } else { + view.getInnerView().clearAccessibilityFocus() + } + } + override fun animateViewIn(view: ViewGroup) { - val chipInnerView = view.requireViewById<ViewGroup>(R.id.chipbar_inner) ViewHierarchyAnimator.animateAddition( - chipInnerView, + view.getInnerView(), ViewHierarchyAnimator.Hotspot.TOP, Interpolators.EMPHASIZED_DECELERATE, duration = ANIMATION_IN_DURATION, includeMargins = true, includeFadeIn = true, // We can only request focus once the animation finishes. - onAnimationEnd = { chipInnerView.requestAccessibilityFocus() }, + onAnimationEnd = { + maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view) + }, ) } override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) { + val innerView = view.getInnerView() + innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE ViewHierarchyAnimator.animateRemoval( - view.requireViewById<ViewGroup>(R.id.chipbar_inner), + innerView, ViewHierarchyAnimator.Hotspot.TOP, Interpolators.EMPHASIZED_ACCELERATE, ANIMATION_OUT_DURATION, @@ -199,6 +221,10 @@ open class ChipbarCoordinator @Inject constructor( ) } + private fun ViewGroup.getInnerView(): ViewGroup { + return requireViewById(R.id.chipbar_inner) + } + override fun start() {} override fun getTouchableRegion(view: View, outRect: Rect) { @@ -216,3 +242,4 @@ open class ChipbarCoordinator @Inject constructor( private const val ANIMATION_IN_DURATION = 500L private const val ANIMATION_OUT_DURATION = 250L +@IdRes private val INFO_TAG = R.id.tag_chipbar_info diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt index 6216acd6081e..101bd4483cb3 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt @@ -16,6 +16,7 @@ package com.android.systemui.unfold +import android.annotation.BinderThread import android.content.Context import android.hardware.devicestate.DeviceStateManager import android.os.PowerManager @@ -41,7 +42,6 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch /** @@ -52,7 +52,7 @@ import kotlinx.coroutines.launch class FoldAodAnimationController @Inject constructor( - @Main private val executor: DelayableExecutor, + @Main private val mainExecutor: DelayableExecutor, private val context: Context, private val deviceStateManager: DeviceStateManager, private val wakefulnessLifecycle: WakefulnessLifecycle, @@ -89,7 +89,7 @@ constructor( override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) { this.centralSurfaces = centralSurfaces - deviceStateManager.registerCallback(executor, FoldListener()) + deviceStateManager.registerCallback(mainExecutor, FoldListener()) wakefulnessLifecycle.addObserver(this) // TODO(b/254878364): remove this call to NPVC.getView() @@ -139,7 +139,8 @@ constructor( * @param onReady callback when the animation is ready * @see [com.android.systemui.keyguard.KeyguardViewMediator] */ - fun onScreenTurningOn(onReady: Runnable) { + @BinderThread + fun onScreenTurningOn(onReady: Runnable) = mainExecutor.execute { if (shouldPlayAnimation) { // The device was not dozing and going to sleep after folding, play the animation @@ -179,12 +180,13 @@ constructor( } } - fun onScreenTurnedOn() { + @BinderThread + fun onScreenTurnedOn() = mainExecutor.execute { if (shouldPlayAnimation) { cancelAnimation?.run() // Post starting the animation to the next frame to avoid junk due to inset changes - cancelAnimation = executor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0) + cancelAnimation = mainExecutor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0) shouldPlayAnimation = false } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt index b2ec27c8ce43..9cca95028729 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.unfold +import android.annotation.BinderThread import android.content.ContentResolver import android.content.Context import android.graphics.PixelFormat @@ -34,9 +35,7 @@ import android.view.SurfaceControlViewHost import android.view.SurfaceSession import android.view.WindowManager import android.view.WindowlessWindowManager -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.LinearLightRevealEffect @@ -45,7 +44,7 @@ import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation.AddOverlayR import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled -import com.android.systemui.util.Assert.isMainThread +import com.android.systemui.util.concurrency.ThreadFactory import com.android.systemui.util.traceSection import com.android.wm.shell.displayareahelper.DisplayAreaHelper import java.util.Optional @@ -64,14 +63,16 @@ constructor( private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider, private val displayAreaHelper: Optional<DisplayAreaHelper>, @Main private val executor: Executor, - @UiBackground private val backgroundExecutor: Executor, - @Background private val bgHandler: Handler, + private val threadFactory: ThreadFactory, private val rotationChangeProvider: RotationChangeProvider, ) { private val transitionListener = TransitionListener() private val rotationWatcher = RotationWatcher() + private lateinit var bgHandler: Handler + private lateinit var bgExecutor: Executor + private lateinit var wwm: WindowlessWindowManager private lateinit var unfoldedDisplayInfo: DisplayInfo private lateinit var overlayContainer: SurfaceControl @@ -84,7 +85,12 @@ constructor( private var currentRotation: Int = context.display!!.rotation fun init() { - deviceStateManager.registerCallback(executor, FoldListener()) + // This method will be called only on devices where this animation is enabled, + // so normally this thread won't be created + bgHandler = threadFactory.buildHandlerOnNewThread(TAG) + bgExecutor = threadFactory.buildDelayableExecutorOnHandler(bgHandler) + + deviceStateManager.registerCallback(bgExecutor, FoldListener()) unfoldTransitionProgressProvider.addCallback(transitionListener) rotationChangeProvider.addCallback(rotationWatcher) @@ -122,20 +128,23 @@ constructor( * @param onOverlayReady callback when the overlay is drawn and visible on the screen * @see [com.android.systemui.keyguard.KeyguardViewMediator] */ + @BinderThread fun onScreenTurningOn(onOverlayReady: Runnable) { - Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn") - try { - // Add the view only if we are unfolding and this is the first screen on - if (!isFolded && !isUnfoldHandled && contentResolver.areAnimationsEnabled()) { - executeInBackground { addOverlay(onOverlayReady, reason = UNFOLD) } - isUnfoldHandled = true - } else { - // No unfold transition, immediately report that overlay is ready - executeInBackground { ensureOverlayRemoved() } - onOverlayReady.run() + executeInBackground { + Trace.beginSection("$TAG#onScreenTurningOn") + try { + // Add the view only if we are unfolding and this is the first screen on + if (!isFolded && !isUnfoldHandled && contentResolver.areAnimationsEnabled()) { + addOverlay(onOverlayReady, reason = UNFOLD) + isUnfoldHandled = true + } else { + // No unfold transition, immediately report that overlay is ready + ensureOverlayRemoved() + onOverlayReady.run() + } + } finally { + Trace.endSection() } - } finally { - Trace.endSection() } } @@ -154,17 +163,18 @@ constructor( LightRevealScrim(context, null).apply { revealEffect = createLightRevealEffect() isScrimOpaqueChangedListener = Consumer {} - revealAmount = when (reason) { - FOLD -> TRANSPARENT - UNFOLD -> BLACK - } + revealAmount = + when (reason) { + FOLD -> TRANSPARENT + UNFOLD -> BLACK + } } val params = getLayoutParams() newRoot.setView(newView, params) if (onOverlayReady != null) { - Trace.beginAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0) + Trace.beginAsyncSection("$TAG#relayout", 0) newRoot.relayout(params) { transaction -> val vsyncId = Choreographer.getSfInstance().vsyncId @@ -179,8 +189,8 @@ constructor( transaction .setFrameTimelineVsync(vsyncId + 1) - .addTransactionCommittedListener(backgroundExecutor) { - Trace.endAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0) + .addTransactionCommittedListener(bgExecutor) { + Trace.endAsyncSection("$TAG#relayout", 0) onOverlayReady.run() } .apply() @@ -233,7 +243,8 @@ constructor( } private fun getUnfoldedDisplayInfo(): DisplayInfo = - displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) + displayManager + .getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) .asSequence() .map { DisplayInfo().apply { it.getDisplayInfo(this) } } .filter { it.type == Display.TYPE_INTERNAL } @@ -261,10 +272,10 @@ constructor( private inner class RotationWatcher : RotationChangeProvider.RotationListener { override fun onRotationChanged(newRotation: Int) { - traceSection("UnfoldLightRevealOverlayAnimation#onRotationChanged") { - if (currentRotation != newRotation) { - currentRotation = newRotation - executeInBackground { + executeInBackground { + traceSection("$TAG#onRotationChanged") { + if (currentRotation != newRotation) { + currentRotation = newRotation scrimView?.revealEffect = createLightRevealEffect() root?.relayout(getLayoutParams()) } @@ -274,7 +285,10 @@ constructor( } private fun executeInBackground(f: () -> Unit) { - ensureInMainThread() + check(Looper.myLooper() != bgHandler.looper) { + "Trying to execute using background handler while already running" + + " in the background handler" + } // The UiBackground executor is not used as it doesn't have a prepared looper. bgHandler.post(f) } @@ -283,25 +297,25 @@ constructor( check(Looper.myLooper() == bgHandler.looper) { "Not being executed in the background!" } } - private fun ensureInMainThread() { - isMainThread() - } - private inner class FoldListener : FoldStateListener( context, Consumer { isFolded -> if (isFolded) { - executeInBackground { ensureOverlayRemoved() } + ensureOverlayRemoved() isUnfoldHandled = false } this.isFolded = isFolded } ) - private enum class AddOverlayReason { FOLD, UNFOLD } + private enum class AddOverlayReason { + FOLD, + UNFOLD + } private companion object { + const val TAG = "UnfoldLightRevealOverlayAnimation" const val ROTATION_ANIMATION_OVERLAY_Z_INDEX = Integer.MAX_VALUE // Put the unfold overlay below the rotation animation screenshot to hide the moment diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index 7da2d47c1226..52b7fb63c1a2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -39,9 +39,9 @@ constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.user_switcher_fullscreen) - window.decorView.getWindowInsetsController().apply { - setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) - hide(Type.systemBars()) + window.decorView.windowInsetsController?.let { controller -> + controller.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + controller.hide(Type.systemBars()) } val viewModel = ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java] diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt index 6cd384f17803..ceebcb77fde2 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt @@ -25,8 +25,11 @@ import java.util.concurrent.atomic.AtomicReference */ class PendingTasksContainer { - private var pendingTasksCount: AtomicInteger = AtomicInteger(0) - private var completionCallback: AtomicReference<Runnable> = AtomicReference() + @Volatile + private var pendingTasksCount = AtomicInteger(0) + + @Volatile + private var completionCallback = AtomicReference<Runnable>() /** * Registers a task that we should wait for diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt deleted file mode 100644 index fa8c8982bccb..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt +++ /dev/null @@ -1,105 +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.keyguard - -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class KeyguardListenQueueTest : SysuiTestCase() { - - @Test - fun testQueueIsBounded() { - val size = 5 - val queue = KeyguardListenQueue(sizePerModality = size) - - val fingerprints = List(100) { fingerprintModel(it) } - fingerprints.forEach { queue.add(it) } - - assertThat(queue.models).containsExactlyElementsIn(fingerprints.takeLast(size)) - - val faces = List(100) { faceModel(it) } - faces.forEach { queue.add(it) } - - assertThat(queue.models).containsExactlyElementsIn( - faces.takeLast(size) + fingerprints.takeLast(5) - ) - - repeat(100) { - queue.add(faceModel(-1)) - queue.add(fingerprintModel(-1)) - } - assertThat(queue.models).hasSize(2 * size) - assertThat(queue.models.count { it.userId == -1 }).isEqualTo(2 * size) - } -} - -private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel( - timeMillis = System.currentTimeMillis(), - userId = user, - listening = false, - biometricEnabledForUser = false, - bouncerIsOrWillShow = false, - canSkipBouncer = false, - credentialAttempted = false, - deviceInteractive = false, - dreaming = false, - fingerprintDisabled = false, - fingerprintLockedOut = false, - goingToSleep = false, - keyguardGoingAway = false, - keyguardIsVisible = false, - keyguardOccluded = false, - occludingAppRequestingFp = false, - primaryUser = false, - shouldListenSfpsState = false, - shouldListenForFingerprintAssistant = false, - strongerAuthRequired = false, - switchingUser = false, - udfps = false, - userDoesNotHaveTrust = false -) - -private fun faceModel(user: Int) = KeyguardFaceListenModel( - timeMillis = System.currentTimeMillis(), - userId = user, - listening = false, - authInterruptActive = false, - biometricSettingEnabledForUser = false, - bouncerFullyShown = false, - faceAndFpNotAuthenticated = false, - faceAuthAllowed = true, - faceDisabled = false, - faceLockedOut = false, - goingToSleep = false, - keyguardAwake = false, - keyguardGoingAway = false, - listeningForFaceAssistant = false, - occludingAppRequestingFaceAuth = false, - primaryUser = false, - secureCameraLaunched = false, - supportsDetect = true, - switchingUser = false, - udfpsBouncerShowing = false, - udfpsFingerDown = false, - userNotTrustedOrDetectionIsNeeded = false -) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 849ff08ac84c..13cd328d00e0 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -823,7 +823,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); lockscreenBypassIsAllowed(); - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, + mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>()); keyguardIsVisible(); @@ -834,7 +834,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() { mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, + mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>()); keyguardIsVisible(); verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), @@ -1049,8 +1049,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testGetUserCanSkipBouncer_whenTrust() { int user = KeyguardUpdateMonitor.getCurrentUser(); - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */, - new ArrayList<>()); + mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, + user, 0 /* flags */, new ArrayList<>()); assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue(); } @@ -1314,7 +1314,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); // WHEN trust is enabled (ie: via smartlock) - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, + mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>()); // THEN we shouldn't listen for udfps @@ -1418,13 +1418,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShowTrustGrantedMessage_onTrustGranted() { // WHEN trust is enabled (ie: via some trust agent) with a trustGranted string - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, + mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, Arrays.asList("Unlocked by wearable")); // THEN the showTrustGrantedMessage should be called with the first message verify(mTestCallback).onTrustGrantedForCurrentUser( - anyBoolean(), + anyBoolean() /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, eq(new TrustGrantFlags(0)), eq("Unlocked by wearable")); } @@ -1870,6 +1871,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD flag mKeyguardUpdateMonitor.onTrustChanged( true /* enabled */, + true /* newlyUnlocked */, getCurrentUser() /* userId */, TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */, null /* trustGrantedMessages */); @@ -1877,6 +1879,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN onTrustGrantedForCurrentUser callback called verify(callback).onTrustGrantedForCurrentUser( eq(true) /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)), eq(null) /* message */ ); @@ -1893,6 +1896,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD flag mKeyguardUpdateMonitor.onTrustChanged( true /* enabled */, + true /* newlyUnlocked */, getCurrentUser() /* userId */, TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */, null /* trustGrantedMessages */); @@ -1900,6 +1904,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN onTrustGrantedForCurrentUser callback called verify(callback).onTrustGrantedForCurrentUser( eq(false) /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)), eq(null) /* message */ ); @@ -1917,6 +1922,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN onTrustChanged for a different user mKeyguardUpdateMonitor.onTrustChanged( true /* enabled */, + true /* newlyUnlocked */, 546 /* userId, not the current userId */, 0 /* flags */, null /* trustGrantedMessages */); @@ -1924,6 +1930,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN onTrustGrantedForCurrentUser callback called verify(callback, never()).onTrustGrantedForCurrentUser( anyBoolean() /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, anyObject() /* flags */, anyString() /* message */ ); @@ -1941,6 +1948,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // flags (temporary & rewable is active unlock) mKeyguardUpdateMonitor.onTrustChanged( true /* enabled */, + true /* newlyUnlocked */, getCurrentUser() /* userId */, TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */, @@ -1949,6 +1957,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN onTrustGrantedForCurrentUser callback called verify(callback).onTrustGrantedForCurrentUser( eq(true) /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)), eq(null) /* message */ @@ -1968,6 +1977,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN onTrustChanged with INITIATED_BY_USER flag mKeyguardUpdateMonitor.onTrustChanged( true /* enabled */, + true /* newlyUnlocked */, getCurrentUser() /* userId, not the current userId */, TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER /* flags */, null /* trustGrantedMessages */); @@ -1975,6 +1985,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN onTrustGrantedForCurrentUser callback called verify(callback, never()).onTrustGrantedForCurrentUser( eq(true) /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER)), anyString() /* message */ ); @@ -1992,6 +2003,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN onTrustChanged with INITIATED_BY_USER flag mKeyguardUpdateMonitor.onTrustChanged( true /* enabled */, + true /* newlyUnlocked */, getCurrentUser() /* userId, not the current userId */, TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */, @@ -2000,6 +2012,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN onTrustGrantedForCurrentUser callback called verify(callback, never()).onTrustGrantedForCurrentUser( eq(true) /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)), anyString() /* message */ @@ -2310,6 +2323,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private void currentUserDoesNotHaveTrust() { mKeyguardUpdateMonitor.onTrustChanged( false, + false, KeyguardUpdateMonitor.getCurrentUser(), -1, new ArrayList<>() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt index 5734c3de70e0..34e78eb8c2eb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt @@ -52,8 +52,6 @@ class ScreenOnCoordinatorTest : SysuiTestCase() { private lateinit var foldAodAnimationController: FoldAodAnimationController @Mock private lateinit var unfoldAnimation: UnfoldLightRevealOverlayAnimation - @Mock - private lateinit var screenLifecycle: ScreenLifecycle @Captor private lateinit var readyCaptor: ArgumentCaptor<Runnable> @@ -69,13 +67,8 @@ class ScreenOnCoordinatorTest : SysuiTestCase() { .thenReturn(foldAodAnimationController) screenOnCoordinator = ScreenOnCoordinator( - screenLifecycle, Optional.of(unfoldComponent), - FakeExecution() ) - - // Make sure screen events are registered to observe - verify(screenLifecycle).addObserver(screenOnCoordinator) } @Test @@ -93,9 +86,7 @@ class ScreenOnCoordinatorTest : SysuiTestCase() { fun testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback() { // Recreate with empty unfoldComponent screenOnCoordinator = ScreenOnCoordinator( - screenLifecycle, Optional.empty(), - FakeExecution() ) screenOnCoordinator.onScreenTurningOn(runnable) @@ -105,11 +96,11 @@ class ScreenOnCoordinatorTest : SysuiTestCase() { private fun onUnfoldOverlayReady() { verify(unfoldAnimation).onScreenTurningOn(capture(readyCaptor)) - readyCaptor.getValue().run() + readyCaptor.value.run() } private fun onFoldAodReady() { verify(foldAodAnimationController).onScreenTurningOn(capture(readyCaptor)) - readyCaptor.getValue().run() + readyCaptor.value.run() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java index d6e621f1f135..b4e85c06933a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java @@ -16,12 +16,14 @@ package com.android.systemui.clipboardoverlay; +import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED; import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -29,10 +31,13 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.animation.Animator; +import android.app.RemoteAction; import android.content.ClipData; import android.content.ClipDescription; +import android.content.Context; import android.net.Uri; import android.os.PersistableBundle; +import android.view.textclassifier.TextLinks; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -42,6 +47,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.screenshot.TimeoutHandler; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.After; import org.junit.Before; @@ -50,7 +57,12 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.Optional; @SmallTest @RunWith(AndroidJUnit4.class) @@ -80,6 +92,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { private ArgumentCaptor<ClipboardOverlayView.ClipboardOverlayCallbacks> mOverlayCallbacksCaptor; private ClipboardOverlayView.ClipboardOverlayCallbacks mCallbacks; + private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -101,6 +115,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mTimeoutHandler, mFeatureFlags, mClipboardUtils, + mExecutor, mUiEventLogger); verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture()); mCallbacks = mOverlayCallbacksCaptor.getValue(); @@ -237,4 +252,29 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "second.package"); verifyNoMoreInteractions(mUiEventLogger); } + + @Test + public void test_logOnClipboardActionsShown() { + ClipData.Item item = mSampleClipData.getItemAt(0); + item.setTextLinks(Mockito.mock(TextLinks.class)); + mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true); + when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString())) + .thenReturn(true); + when(mClipboardUtils.getAction(any(ClipData.Item.class), anyString())) + .thenReturn(Optional.of(Mockito.mock(RemoteAction.class))); + when(mClipboardOverlayView.post(any(Runnable.class))).thenAnswer(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + ((Runnable) invocation.getArgument(0)).run(); + return null; + } + }); + + mOverlayController.setClipData( + new ClipData(mSampleClipData.getDescription(), item), "actionShownSource"); + mExecutor.runAllReady(); + + verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_ACTION_SHOWN, 0, "actionShownSource"); + verifyNoMoreInteractions(mUiEventLogger); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java index 09b1699d3ffc..aea6be3d468b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java @@ -16,13 +16,24 @@ package com.android.systemui.clipboardoverlay; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.when; +import android.app.RemoteAction; import android.content.ClipData; import android.content.ClipDescription; import android.os.PersistableBundle; import android.testing.TestableResources; +import android.util.ArrayMap; +import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationManager; +import android.view.textclassifier.TextClassifier; +import android.view.textclassifier.TextLinks; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -30,19 +41,84 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.google.android.collect.Lists; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Map; +import java.util.Optional; @SmallTest @RunWith(AndroidJUnit4.class) public class ClipboardOverlayUtilsTest extends SysuiTestCase { private ClipboardOverlayUtils mClipboardUtils; + @Mock + private TextClassificationManager mTextClassificationManager; + @Mock + private TextClassifier mTextClassifier; + + @Mock + private ClipData.Item mClipDataItem; @Before public void setUp() { - mClipboardUtils = new ClipboardOverlayUtils(); + MockitoAnnotations.initMocks(this); + + when(mTextClassificationManager.getTextClassifier()).thenReturn(mTextClassifier); + mClipboardUtils = new ClipboardOverlayUtils(mTextClassificationManager); + } + + @Test + public void test_getAction_noLinks_returnsEmptyOptional() { + ClipData.Item item = new ClipData.Item("no text links"); + item.setTextLinks(Mockito.mock(TextLinks.class)); + + Optional<RemoteAction> action = mClipboardUtils.getAction(item, ""); + + assertTrue(action.isEmpty()); + } + + @Test + public void test_getAction_returnsFirstLink() { + when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks()); + when(mClipDataItem.getText()).thenReturn(""); + RemoteAction actionA = constructRemoteAction("abc"); + RemoteAction actionB = constructRemoteAction("def"); + TextClassification classificationA = Mockito.mock(TextClassification.class); + when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); + TextClassification classificationB = Mockito.mock(TextClassification.class); + when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); + when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( + classificationA, classificationB); + + RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "def").orElse(null); + + assertEquals(actionA, result); + } + + @Test + public void test_getAction_skipsMatchingComponent() { + when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks()); + when(mClipDataItem.getText()).thenReturn(""); + RemoteAction actionA = constructRemoteAction("abc"); + RemoteAction actionB = constructRemoteAction("def"); + TextClassification classificationA = Mockito.mock(TextClassification.class); + when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); + TextClassification classificationB = Mockito.mock(TextClassification.class); + when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); + when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( + classificationA, classificationB); + + RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "abc").orElse(null); + + assertEquals(actionB, result); } @Test @@ -92,7 +168,7 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "")); } - static ClipData constructClipData(String[] mimeTypes, ClipData.Item item, + private static ClipData constructClipData(String[] mimeTypes, ClipData.Item item, PersistableBundle extras) { ClipDescription description = new ClipDescription("Test", mimeTypes); if (extras != null) { @@ -100,4 +176,20 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { } return new ClipData(description, item); } + + private static RemoteAction constructRemoteAction(String packageName) { + RemoteAction action = Mockito.mock(RemoteAction.class, Answers.RETURNS_DEEP_STUBS); + when(action.getActionIntent().getIntent().getComponent().getPackageName()) + .thenReturn(packageName); + return action; + } + + private static TextLinks getFakeTextLinks() { + TextLinks.Builder textLinks = new TextLinks.Builder("test"); + final Map<String, Float> scores = new ArrayMap<>(); + scores.put(TextClassifier.TYPE_EMAIL, 1f); + textLinks.addLink(0, 0, scores); + textLinks.addLink(0, 0, scores); + return textLinks.build(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java index f46d58d679b5..70a0415d2e35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java @@ -58,15 +58,15 @@ public class ScreenLifecycleTest extends SysuiTestCase { @Test public void screenTurningOn() throws Exception { Runnable onDrawn = () -> {}; - mScreen.dispatchScreenTurningOn(onDrawn); + mScreen.dispatchScreenTurningOn(); assertEquals(ScreenLifecycle.SCREEN_TURNING_ON, mScreen.getScreenState()); - verify(mScreenObserverMock).onScreenTurningOn(onDrawn); + verify(mScreenObserverMock).onScreenTurningOn(); } @Test public void screenTurnedOn() throws Exception { - mScreen.dispatchScreenTurningOn(null); + mScreen.dispatchScreenTurningOn(); mScreen.dispatchScreenTurnedOn(); assertEquals(ScreenLifecycle.SCREEN_ON, mScreen.getScreenState()); @@ -75,7 +75,7 @@ public class ScreenLifecycleTest extends SysuiTestCase { @Test public void screenTurningOff() throws Exception { - mScreen.dispatchScreenTurningOn(null); + mScreen.dispatchScreenTurningOn(); mScreen.dispatchScreenTurnedOn(); mScreen.dispatchScreenTurningOff(); @@ -85,7 +85,7 @@ public class ScreenLifecycleTest extends SysuiTestCase { @Test public void screenTurnedOff() throws Exception { - mScreen.dispatchScreenTurningOn(null); + mScreen.dispatchScreenTurningOn(); mScreen.dispatchScreenTurnedOn(); mScreen.dispatchScreenTurningOff(); mScreen.dispatchScreenTurnedOff(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt index 67091a9f40c7..3d6571349a3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt @@ -165,6 +165,10 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { @Test fun `remembers selections by user`() = runTest { + overrideResource( + R.array.config_keyguardQuickAffordanceDefaults, + arrayOf<String>(), + ) val slot1 = "slot_1" val slot2 = "slot_2" val affordance1 = "affordance_1" diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index c40488adf029..c187a3ff63bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -114,6 +114,11 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { userHandle = UserHandle.SYSTEM, ) + overrideResource( + R.array.config_keyguardQuickAffordanceDefaults, + arrayOf<String>(), + ) + underTest = KeyguardQuickAffordanceRepository( appContext = context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index ce9c1da422f5..5d2f0eb01de1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -16,13 +16,10 @@ package com.android.systemui.keyguard.data.repository -import android.animation.AnimationHandler.AnimationFrameCallbackProvider import android.animation.ValueAnimator import android.util.Log import android.util.Log.TerribleFailure import android.util.Log.TerribleFailureHandler -import android.view.Choreographer.FrameCallback -import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Interpolators @@ -32,22 +29,17 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.keyguard.util.KeyguardTransitionRunner import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import java.math.RoundingMode import java.util.UUID -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.yield +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.After -import org.junit.Assert.fail import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -60,12 +52,14 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { private lateinit var underTest: KeyguardTransitionRepository private lateinit var oldWtfHandler: TerribleFailureHandler private lateinit var wtfHandler: WtfHandler + private lateinit var runner: KeyguardTransitionRunner @Before fun setUp() { underTest = KeyguardTransitionRepositoryImpl() wtfHandler = WtfHandler() oldWtfHandler = Log.setWtfHandler(wtfHandler) + runner = KeyguardTransitionRunner(underTest) } @After @@ -75,56 +69,37 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { @Test fun `startTransition runs animator to completion`() = - runBlocking(IMMEDIATE) { - val (animator, provider) = setupAnimator(this) - + TestScope().runTest { val steps = mutableListOf<TransitionStep>() val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this) - underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator)) - - val startTime = System.currentTimeMillis() - while (animator.isRunning()) { - yield() - if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) { - fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION") - } - } + runner.startTransition( + this, + TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), + maxFrames = 100 + ) assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN) - job.cancel() - provider.stop() } @Test - @FlakyTest(bugId = 260213291) - fun `starting second transition will cancel the first transition`() { - runBlocking(IMMEDIATE) { - val (animator, provider) = setupAnimator(this) - + fun `starting second transition will cancel the first transition`() = + TestScope().runTest { val steps = mutableListOf<TransitionStep>() val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this) - - underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator)) - // 3 yields(), alternating with the animator, results in a value 0.1, which can be - // canceled and tested against - yield() - yield() - yield() + runner.startTransition( + this, + TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), + maxFrames = 3, + ) // Now start 2nd transition, which will interrupt the first val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this) - val (animator2, provider2) = setupAnimator(this) - underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, animator2)) - - val startTime = System.currentTimeMillis() - while (animator2.isRunning()) { - yield() - if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) { - fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION") - } - } + runner.startTransition( + this, + TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, getAnimator()), + ) val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1)) assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN) @@ -134,31 +109,25 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { job.cancel() job2.cancel() - provider.stop() - provider2.stop() } - } @Test fun `Null animator enables manual control with updateTransition`() = - runBlocking(IMMEDIATE) { + TestScope().runTest { val steps = mutableListOf<TransitionStep>() val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this) val uuid = underTest.startTransition( - TransitionInfo( - ownerName = OWNER_NAME, - from = AOD, - to = LOCKSCREEN, - animator = null, - ) + TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null) ) + runCurrent() checkNotNull(uuid).let { underTest.updateTransition(it, 0.5f, TransitionState.RUNNING) underTest.updateTransition(it, 1f, TransitionState.FINISHED) } + runCurrent() assertThat(steps.size).isEqualTo(3) assertThat(steps[0]) @@ -256,57 +225,11 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { assertThat(wtfHandler.failed).isFalse() } - private fun setupAnimator( - scope: CoroutineScope - ): Pair<ValueAnimator, TestFrameCallbackProvider> { - val animator = - ValueAnimator().apply { - setInterpolator(Interpolators.LINEAR) - setDuration(ANIMATION_DURATION) - } - - val provider = TestFrameCallbackProvider(animator, scope) - provider.start() - - return Pair(animator, provider) - } - - /** Gives direct control over ValueAnimator. See [AnimationHandler] */ - private class TestFrameCallbackProvider( - private val animator: ValueAnimator, - private val scope: CoroutineScope, - ) : AnimationFrameCallbackProvider { - - private var frameCount = 1L - private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null)) - private var job: Job? = null - - fun start() { - animator.getAnimationHandler().setProvider(this) - - job = - scope.launch { - frames.collect { - // Delay is required for AnimationHandler to properly register a callback - yield() - val (frameNumber, callback) = it - callback?.doFrame(frameNumber) - } - } - } - - fun stop() { - job?.cancel() - animator.getAnimationHandler().setProvider(null) - } - - override fun postFrameCallback(cb: FrameCallback) { - frames.value = Pair(frameCount++, cb) + private fun getAnimator(): ValueAnimator { + return ValueAnimator().apply { + setInterpolator(Interpolators.LINEAR) + setDuration(10) } - override fun postCommitCallback(runnable: Runnable) {} - override fun getFrameTime() = frameCount - override fun getFrameDelay() = 1L - override fun setFrameDelay(delay: Long) {} } private class WtfHandler : TerribleFailureHandler { @@ -317,9 +240,6 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } companion object { - private const val MAX_TEST_DURATION = 100L - private const val ANIMATION_DURATION = 10L - private const val OWNER_NAME = "Test" - private val IMMEDIATE = Dispatchers.Main.immediate + private const val OWNER_NAME = "KeyguardTransitionRunner" } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index d97571bcd8ef..58a85306eb69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.domain.interactor import android.os.UserHandle import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -290,6 +291,11 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Test fun select() = testScope.runTest { + overrideResource( + R.array.config_keyguardQuickAffordanceDefaults, + arrayOf<String>(), + ) + featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) homeControls.setState( KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt new file mode 100644 index 000000000000..a6cf84053861 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -0,0 +1,140 @@ +/* + * 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.keyguard.domain.interactor + +import android.animation.ValueAnimator +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.Interpolators +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionInfo +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState +import com.android.systemui.keyguard.util.KeyguardTransitionRunner +import com.android.systemui.shade.data.repository.FakeShadeRepository +import com.android.systemui.shade.data.repository.ShadeRepository +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +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.MockitoAnnotations + +/** + * Class for testing user journeys through the interactors. They will all be activated during setup, + * to ensure the expected transitions are still triggered. + */ +@SmallTest +@RunWith(JUnit4::class) +class KeyguardTransitionScenariosTest : SysuiTestCase() { + private lateinit var testScope: TestScope + + private lateinit var keyguardRepository: FakeKeyguardRepository + private lateinit var shadeRepository: ShadeRepository + + // Used to issue real transition steps for test input + private lateinit var runner: KeyguardTransitionRunner + private lateinit var transitionRepository: KeyguardTransitionRepository + + // Used to verify transition requests for test output + @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository + + private lateinit var lockscreenBouncerTransitionInteractor: + LockscreenBouncerTransitionInteractor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testScope = TestScope() + + keyguardRepository = FakeKeyguardRepository() + shadeRepository = FakeShadeRepository() + + /* Used to issue full transition steps, to better simulate a real device */ + transitionRepository = KeyguardTransitionRepositoryImpl() + runner = KeyguardTransitionRunner(transitionRepository) + + lockscreenBouncerTransitionInteractor = + LockscreenBouncerTransitionInteractor( + scope = testScope, + keyguardInteractor = KeyguardInteractor(keyguardRepository), + shadeRepository = shadeRepository, + keyguardTransitionRepository = mockTransitionRepository, + keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), + ) + lockscreenBouncerTransitionInteractor.start() + } + + @Test + fun `LOCKSCREEN to BOUNCER via bouncer showing call`() = + testScope.runTest { + // GIVEN a device that has at least woken up + keyguardRepository.setWakefulnessModel(startingToWake()) + runCurrent() + + // GIVEN a transition has run to LOCKSCREEN + runner.startTransition( + testScope, + TransitionInfo( + ownerName = "", + from = KeyguardState.OFF, + to = KeyguardState.LOCKSCREEN, + animator = + ValueAnimator().apply { + duration = 10 + interpolator = Interpolators.LINEAR + }, + ) + ) + runCurrent() + + // WHEN the bouncer is set to show + keyguardRepository.setBouncerShowing(true) + runCurrent() + + val info = + withArgCaptor<TransitionInfo> { + verify(mockTransitionRepository).startTransition(capture()) + } + // THEN a transition to BOUNCER should occur + assertThat(info.ownerName).isEqualTo("LockscreenBouncerTransitionInteractor") + assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN) + assertThat(info.to).isEqualTo(KeyguardState.BOUNCER) + assertThat(info.animator).isNotNull() + + coroutineContext.cancelChildren() + } + + private fun startingToWake() = + WakefulnessModel( + WakefulnessState.STARTING_TO_WAKE, + true, + WakeSleepReason.OTHER, + WakeSleepReason.OTHER + ) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt new file mode 100644 index 000000000000..c88f84a028ed --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt @@ -0,0 +1,101 @@ +/* + * 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.keyguard.util + +import android.animation.AnimationHandler.AnimationFrameCallbackProvider +import android.animation.ValueAnimator +import android.view.Choreographer.FrameCallback +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.TransitionInfo +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.junit.Assert.fail + +/** + * Gives direct control over ValueAnimator, in order to make transition tests deterministic. See + * [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly. + */ +class KeyguardTransitionRunner( + val repository: KeyguardTransitionRepository, +) : AnimationFrameCallbackProvider { + + private var frameCount = 1L + private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null)) + private var job: Job? = null + private var isTerminated = false + + /** + * For transitions being directed by an animator. Will control the number of frames being + * generated so the values are deterministic. + */ + suspend fun startTransition(scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100) { + // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main + // thread + withContext(Dispatchers.Main) { + info.animator!!.getAnimationHandler().setProvider(this@KeyguardTransitionRunner) + } + + job = + scope.launch { + frames.collect { + val (frameNumber, callback) = it + + isTerminated = frameNumber >= maxFrames + if (!isTerminated) { + withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) } + } + } + } + withContext(Dispatchers.Main) { repository.startTransition(info) } + + waitUntilComplete(info.animator!!) + } + + suspend private fun waitUntilComplete(animator: ValueAnimator) { + withContext(Dispatchers.Main) { + val startTime = System.currentTimeMillis() + while (!isTerminated && animator.isRunning()) { + delay(1) + if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) { + fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION") + } + } + + animator.getAnimationHandler().setProvider(null) + } + + job?.cancel() + } + + override fun postFrameCallback(cb: FrameCallback) { + frames.value = Pair(frameCount++, cb) + } + override fun postCommitCallback(runnable: Runnable) {} + override fun getFrameTime() = frameCount + override fun getFrameDelay() = 1L + override fun setFrameDelay(delay: Long) {} + + companion object { + private const val MAX_TEST_DURATION = 100L + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index ffa261a26279..ef0bfb7b6700 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -195,6 +195,36 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { } @Test + fun commandQueueCallback_transferToReceiverSucceeded_noChipShown() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + routeInfo, + null, + null + ) + + verify(windowManager, never()).addView(any(), any()) + assertThat(uiEventLoggerFake.eventId(0)).isEqualTo( + MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_SUCCEEDED.id + ) + } + + @Test + fun commandQueueCallback_transferToReceiverFailed_noChipShown() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED, + routeInfo, + null, + null + ) + + verify(windowManager, never()).addView(any(), any()) + assertThat(uiEventLoggerFake.eventId(0)).isEqualTo( + MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED.id + ) + } + + @Test fun commandQueueCallback_closeThenFar_chipShownThenHidden() { commandQueueCallback.updateMediaTapToTransferReceiverDisplay( StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, @@ -216,6 +246,48 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { } @Test + fun commandQueueCallback_closeThenSucceeded_chipShownThenHidden() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo, + null, + null + ) + + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + routeInfo, + null, + null + ) + + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).removeView(viewCaptor.value) + } + + @Test + fun commandQueueCallback_closeThenFailed_chipShownThenHidden() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo, + null, + null + ) + + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED, + routeInfo, + null, + null + ) + + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).removeView(viewCaptor.value) + } + + @Test fun commandQueueCallback_closeThenFar_wakeLockAcquiredThenReleased() { commandQueueCallback.updateMediaTapToTransferReceiverDisplay( StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS new file mode 100644 index 000000000000..7ccb316dbca5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 1254381 +azappone@google.com +achalke@google.com +juliacr@google.com +madym@google.com +mgalhardo@google.com +petrcermak@google.com +vanjan@google.com
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index caf8321949ca..5058373e39b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -226,7 +226,8 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { + " " + mockTileViewString + "\n" + " media bounds: null\n" + " horizontal layout: false\n" - + " last orientation: 0\n"; + + " last orientation: 0\n" + + " mShouldUseSplitNotificationShade: false\n"; assertEquals(expected, w.getBuffer().toString()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt index 5e082f686ea3..6cf642cb7fd2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt @@ -135,10 +135,10 @@ class QSPanelControllerTest : SysuiTestCase() { fun configurationChange_onlySplitShadeConfigChanges_tileAreRedistributed() { testableResources.addOverride(R.bool.config_use_split_notification_shade, false) controller.mOnConfigurationChangedListener.onConfigurationChange(configuration) - verify(pagedTileLayout, never()).forceTilesRedistribution() + verify(pagedTileLayout, never()).forceTilesRedistribution(any()) testableResources.addOverride(R.bool.config_use_split_notification_shade, true) controller.mOnConfigurationChangedListener.onConfigurationChange(configuration) - verify(pagedTileLayout).forceTilesRedistribution() + verify(pagedTileLayout).forceTilesRedistribution("Split shade state changed") } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index 7c930b196d68..d52b29642acf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -27,6 +27,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSIconViewImpl import com.android.systemui.qs.tileimpl.QSTileViewImpl import com.google.common.truth.Truth.assertThat @@ -34,6 +35,7 @@ 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.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -42,6 +44,9 @@ import org.mockito.MockitoAnnotations @RunWithLooper @SmallTest class QSPanelTest : SysuiTestCase() { + + @Mock private lateinit var qsLogger: QSLogger + private lateinit var testableLooper: TestableLooper private lateinit var qsPanel: QSPanel @@ -57,7 +62,7 @@ class QSPanelTest : SysuiTestCase() { qsPanel = QSPanel(context, null) qsPanel.mUsingMediaPlayer = true - qsPanel.initialize() + qsPanel.initialize(qsLogger) // QSPanel inflates a footer inside of it, mocking it here footer = LinearLayout(context).apply { id = R.id.qs_footer } qsPanel.addView(footer, MATCH_PARENT, 100) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt index a6a584d2e622..3fba3938db19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt @@ -7,10 +7,12 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.logging.QSLogger import com.google.common.truth.Truth 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 @@ -19,6 +21,8 @@ import org.mockito.MockitoAnnotations @SmallTest class QuickQSPanelTest : SysuiTestCase() { + @Mock private lateinit var qsLogger: QSLogger + private lateinit var testableLooper: TestableLooper private lateinit var quickQSPanel: QuickQSPanel @@ -32,7 +36,7 @@ class QuickQSPanelTest : SysuiTestCase() { testableLooper.runWithLooper { quickQSPanel = QuickQSPanel(mContext, null) - quickQSPanel.initialize() + quickQSPanel.initialize(qsLogger) quickQSPanel.onFinishInflate() // Provides a parent with non-zero size for QSPanel 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 c280ec8c4ec8..8d96932f0051 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -1042,7 +1042,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { // GIVEN a trust granted message but trust isn't granted final String trustGrantedMsg = "testing trust granted message"; mController.getKeyguardCallback().onTrustGrantedForCurrentUser( - false, new TrustGrantFlags(0), trustGrantedMsg); + false, false, new TrustGrantFlags(0), trustGrantedMsg); verifyHideIndication(INDICATION_TYPE_TRUST); @@ -1067,7 +1067,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { // WHEN the showTrustGranted method is called final String trustGrantedMsg = "testing trust granted message"; mController.getKeyguardCallback().onTrustGrantedForCurrentUser( - false, new TrustGrantFlags(0), trustGrantedMsg); + false, false, new TrustGrantFlags(0), trustGrantedMsg); // THEN verify the trust granted message shows verifyIndicationMessage( @@ -1085,7 +1085,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { // WHEN the showTrustGranted method is called with a null message mController.getKeyguardCallback().onTrustGrantedForCurrentUser( - false, new TrustGrantFlags(0), null); + false, false, new TrustGrantFlags(0), null); // THEN verify the default trust granted message shows verifyIndicationMessage( @@ -1103,7 +1103,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { // WHEN the showTrustGranted method is called with an EMPTY string mController.getKeyguardCallback().onTrustGrantedForCurrentUser( - false, new TrustGrantFlags(0), ""); + false, false, new TrustGrantFlags(0), ""); // THEN verify NO trust message is shown verifyNoMessage(INDICATION_TYPE_TRUST); 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 5f19fac8666a..be6b1dc8e4a4 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 @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification +import android.os.UserHandle +import android.provider.Settings import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -29,6 +31,7 @@ import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider @@ -38,6 +41,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -47,6 +51,8 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.same +import org.mockito.Mockito.anyString import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import java.util.function.Consumer @@ -176,6 +182,42 @@ class KeyguardCoordinatorTest : SysuiTestCase() { } @Test + fun unseenFilterInvalidatesWhenSettingChanges() { + whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) + + // GIVEN: Keyguard is not showing + keyguardRepository.setKeyguardShowing(false) + runKeyguardCoordinatorTest { + // GIVEN: A notification is present + val fakeEntry = NotificationEntryBuilder().build() + collectionListener.onEntryAdded(fakeEntry) + + // GIVEN: The setting for filtering unseen notifications is disabled + showOnlyUnseenNotifsOnKeyguardSetting = false + + // GIVEN: The pipeline has registered the unseen filter for invalidation + val invalidationListener: Pluggable.PluggableListener<NotifFilter> = mock() + unseenFilter.setInvalidationListener(invalidationListener) + + // WHEN: The keyguard is now showing + keyguardRepository.setKeyguardShowing(true) + testScheduler.runCurrent() + + // THEN: The notification is not filtered out + assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse() + + // WHEN: The secure setting is changed + showOnlyUnseenNotifsOnKeyguardSetting = true + + // THEN: The pipeline is invalidated + verify(invalidationListener).onPluggableInvalidated(same(unseenFilter), anyString()) + + // THEN: The notification is recognized as "seen" and is filtered out. + assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue() + } + } + + @Test fun unseenFilterAllowsNewNotif() { whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) @@ -276,22 +318,32 @@ class KeyguardCoordinatorTest : SysuiTestCase() { private fun runKeyguardCoordinatorTest( testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit ) { - val testScope = TestScope(UnconfinedTestDispatcher()) + val testDispatcher = UnconfinedTestDispatcher() + val testScope = TestScope(testDispatcher) + val fakeSettings = FakeSettings().apply { + putBool(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, true) + } val seenNotificationsProvider = SeenNotificationsProviderImpl() val keyguardCoordinator = KeyguardCoordinator( + testDispatcher, keyguardNotifVisibilityProvider, keyguardRepository, notifPipelineFlags, testScope.backgroundScope, sectionHeaderVisibilityProvider, + fakeSettings, seenNotificationsProvider, statusBarStateController, ) keyguardCoordinator.attach(notifPipeline) testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) { - KeyguardCoordinatorTestScope(keyguardCoordinator, testScope, seenNotificationsProvider) - .testBlock() + KeyguardCoordinatorTestScope( + keyguardCoordinator, + testScope, + seenNotificationsProvider, + fakeSettings, + ).testBlock() } } @@ -299,6 +351,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { private val keyguardCoordinator: KeyguardCoordinator, private val scope: TestScope, val seenNotificationsProvider: SeenNotificationsProvider, + private val fakeSettings: FakeSettings, ) : CoroutineScope by scope { val testScheduler: TestCoroutineScheduler get() = scope.testScheduler @@ -316,5 +369,19 @@ class KeyguardCoordinatorTest : SysuiTestCase() { val collectionListener: NotifCollectionListener by lazy { withArgCaptor { verify(notifPipeline).addCollectionListener(capture()) } } + + var showOnlyUnseenNotifsOnKeyguardSetting: Boolean + get() = + fakeSettings.getBoolForUser( + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + UserHandle.USER_CURRENT, + ) + set(value) { + fakeSettings.putBoolForUser( + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + value, + UserHandle.USER_CURRENT, + ) + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt new file mode 100644 index 000000000000..a6a9e51aa555 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt @@ -0,0 +1,210 @@ +/* + * 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.notification.fsi + +import android.R +import android.app.Notification +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable +import android.os.UserHandle +import android.service.dreams.IDreamManager +import android.service.notification.StatusBarNotification +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider +import com.android.systemui.statusbar.phone.CentralSurfaces +import java.util.concurrent.Executor +import junit.framework.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class FsiChromeRepoTest : SysuiTestCase() { + + @Mock lateinit var centralSurfaces: CentralSurfaces + @Mock lateinit var fsiChromeRepo: FsiChromeRepo + @Mock lateinit var packageManager: PackageManager + + var keyguardRepo = FakeKeyguardRepository() + @Mock private lateinit var applicationInfo: ApplicationInfo + + @Mock lateinit var launchFullScreenIntentProvider: LaunchFullScreenIntentProvider + var featureFlags = FakeFeatureFlags() + @Mock lateinit var dreamManager: IDreamManager + + // Execute all foreground & background requests immediately + private val uiBgExecutor = Executor { r -> r.run() } + + private val appName: String = "appName" + private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android) + private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + // Set up package manager mocks + whenever(packageManager.getApplicationIcon(anyString())).thenReturn(appIcon) + whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) + .thenReturn(appIcon) + whenever(packageManager.getApplicationLabel(any())).thenReturn(appName) + mContext.setMockPackageManager(packageManager) + + fsiChromeRepo = + FsiChromeRepo( + mContext, + packageManager, + keyguardRepo, + launchFullScreenIntentProvider, + featureFlags, + uiBgExecutor, + dreamManager, + centralSurfaces + ) + } + + private fun createFsiEntry(fsi: PendingIntent): NotificationEntry { + val nb = + Notification.Builder(mContext, "a") + .setContentTitle("foo") + .setSmallIcon(R.drawable.sym_def_app_icon) + .setFullScreenIntent(fsi, /* highPriority= */ true) + + val sbn = + StatusBarNotification( + "pkg", + "opPkg", + /* id= */ 0, + "tag" + System.currentTimeMillis(), + /* uid= */ 0, + /* initialPid */ 0, + nb.build(), + UserHandle(0), + /* overrideGroupKey= */ null, + /* postTime= */ 0 + ) + + val entry = Mockito.mock(NotificationEntry::class.java) + whenever(entry.importance).thenReturn(NotificationManager.IMPORTANCE_HIGH) + whenever(entry.sbn).thenReturn(sbn) + return entry + } + + @Test + fun testLaunchFullscreenIntent_flagNotEnabled_noLaunch() { + // Setup + featureFlags.set(Flags.FSI_CHROME, false) + + // Test + val entry = createFsiEntry(fsi) + fsiChromeRepo.launchFullscreenIntent(entry) + + // Verify + Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent() + } + + @Test + fun testLaunchFullscreenIntent_notOnKeyguard_noLaunch() { + // Setup + featureFlags.set(Flags.FSI_CHROME, true) + keyguardRepo.setKeyguardShowing(false) + + // Test + val entry = createFsiEntry(fsi) + fsiChromeRepo.launchFullscreenIntent(entry) + + // Verify + Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent() + } + + @Test + fun testLaunchFullscreenIntent_stopsScreensaver() { + // Setup + featureFlags.set(Flags.FSI_CHROME, true) + keyguardRepo.setKeyguardShowing(true) + + // Test + val entry = createFsiEntry(fsi) + fsiChromeRepo.launchFullscreenIntent(entry) + + // Verify + Mockito.verify(dreamManager, times(1)).awaken() + } + + @Test + fun testLaunchFullscreenIntent_updatesFsiInfoFlow() { + // Setup + featureFlags.set(Flags.FSI_CHROME, true) + keyguardRepo.setKeyguardShowing(true) + + // Test + val entry = createFsiEntry(fsi) + fsiChromeRepo.launchFullscreenIntent(entry) + + // Verify + val expectedFsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi) + assertEquals(expectedFsiInfo, fsiChromeRepo.infoFlow.value) + } + + @Test + fun testLaunchFullscreenIntent_notifyFsiLaunched() { + // Setup + featureFlags.set(Flags.FSI_CHROME, true) + keyguardRepo.setKeyguardShowing(true) + + // Test + val entry = createFsiEntry(fsi) + fsiChromeRepo.launchFullscreenIntent(entry) + + // Verify + Mockito.verify(entry, times(1)).notifyFullScreenIntentLaunched() + } + + @Test + fun testLaunchFullscreenIntent_wakesUpDevice() { + // Setup + featureFlags.set(Flags.FSI_CHROME, true) + keyguardRepo.setKeyguardShowing(true) + + // Test + val entry = createFsiEntry(fsi) + fsiChromeRepo.launchFullscreenIntent(entry) + + // Verify + Mockito.verify(centralSurfaces, times(1)).wakeUpForFullScreenIntent() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt new file mode 100644 index 000000000000..33b94e39c019 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt @@ -0,0 +1,127 @@ +/* + * 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.notification.logging + +import android.app.Notification +import android.app.StatsManager +import android.graphics.Bitmap +import android.graphics.drawable.Icon +import android.testing.AndroidTestingRunner +import android.util.StatsEvent +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.shared.system.SysUiStatsLog +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class NotificationMemoryLoggerTest : SysuiTestCase() { + + private val bgExecutor = FakeExecutor(FakeSystemClock()) + private val immediate = Dispatchers.Main.immediate + + @Mock private lateinit var statsManager: StatsManager + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun onInit_registersCallback() { + val logger = createLoggerWithNotifications(listOf()) + logger.init() + verify(statsManager) + .setPullAtomCallback(SysUiStatsLog.NOTIFICATION_MEMORY_USE, null, bgExecutor, logger) + } + + @Test + fun onPullAtom_wrongAtomId_returnsSkip() { + val logger = createLoggerWithNotifications(listOf()) + val data: MutableList<StatsEvent> = mutableListOf() + assertThat(logger.onPullAtom(111, data)).isEqualTo(StatsManager.PULL_SKIP) + assertThat(data).isEmpty() + } + + @Test + fun onPullAtom_emptyNotifications_returnsZeros() { + val logger = createLoggerWithNotifications(listOf()) + val data: MutableList<StatsEvent> = mutableListOf() + assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, data)) + .isEqualTo(StatsManager.PULL_SUCCESS) + assertThat(data).isEmpty() + } + + @Test + fun onPullAtom_notificationPassed_populatesData() { + val icon = Icon.createWithBitmap(Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888)) + val notification = + Notification.Builder(context).setSmallIcon(icon).setContentTitle("title").build() + val logger = createLoggerWithNotifications(listOf(notification)) + val data: MutableList<StatsEvent> = mutableListOf() + + assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, data)) + .isEqualTo(StatsManager.PULL_SUCCESS) + assertThat(data).hasSize(1) + } + + @Test + fun onPullAtom_multipleNotificationsPassed_populatesData() { + val icon = Icon.createWithBitmap(Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888)) + val notification = + Notification.Builder(context).setSmallIcon(icon).setContentTitle("title").build() + val iconTwo = Icon.createWithBitmap(Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888)) + + val notificationTwo = + Notification.Builder(context) + .setStyle(Notification.BigTextStyle().bigText("text")) + .setSmallIcon(iconTwo) + .setContentTitle("titleTwo") + .build() + val logger = createLoggerWithNotifications(listOf(notification, notificationTwo)) + val data: MutableList<StatsEvent> = mutableListOf() + + assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, data)) + .isEqualTo(StatsManager.PULL_SUCCESS) + assertThat(data).hasSize(2) + } + + private fun createLoggerWithNotifications( + notifications: List<Notification> + ): NotificationMemoryLogger { + val pipeline: NotifPipeline = mock() + val notifications = + notifications.map { notification -> + NotificationEntryBuilder().setTag("test").setNotification(notification).build() + } + whenever(pipeline.allNotifs).thenReturn(notifications) + return NotificationMemoryLogger(pipeline, statsManager, immediate, bgExecutor) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt index f69839b7087c..072a497f1a65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt @@ -23,6 +23,7 @@ import android.app.Person import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Icon +import android.stats.sysui.NotificationEnums import android.testing.AndroidTestingRunner import android.widget.RemoteViews import androidx.test.filters.SmallTest @@ -50,7 +51,27 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 3316, bigPicture = 0, extender = 0, - style = null, + style = NotificationEnums.STYLE_NONE, + styleIcon = 0, + hasCustomView = false, + ) + } + + @Test + fun currentNotificationMemoryUse_rankerGroupNotification() { + val notification = createBasicNotification().build() + val memoryUse = + NotificationMemoryMeter.notificationMemoryUse( + createNotificationEntry(createBasicNotification().setGroup("ranker_group").build()) + ) + assertNotificationObjectSizes( + memoryUse, + smallIcon = notification.smallIcon.bitmap.allocationByteCount, + largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, + extras = 3316, + bigPicture = 0, + extender = 0, + style = NotificationEnums.STYLE_RANKER_GROUP, styleIcon = 0, hasCustomView = false, ) @@ -69,7 +90,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 3316, bigPicture = 0, extender = 0, - style = null, + style = NotificationEnums.STYLE_NONE, styleIcon = 0, hasCustomView = false, ) @@ -92,7 +113,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 3384, bigPicture = 0, extender = 0, - style = null, + style = NotificationEnums.STYLE_NONE, styleIcon = 0, hasCustomView = true, ) @@ -112,7 +133,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 3212, bigPicture = 0, extender = 0, - style = null, + style = NotificationEnums.STYLE_NONE, styleIcon = 0, hasCustomView = false, ) @@ -141,7 +162,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 4092, bigPicture = bigPicture.bitmap.allocationByteCount, extender = 0, - style = "BigPictureStyle", + style = NotificationEnums.STYLE_BIG_PICTURE, styleIcon = bigPictureIcon.bitmap.allocationByteCount, hasCustomView = false, ) @@ -167,7 +188,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 4084, bigPicture = 0, extender = 0, - style = "CallStyle", + style = NotificationEnums.STYLE_CALL, styleIcon = personIcon.bitmap.allocationByteCount, hasCustomView = false, ) @@ -203,7 +224,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 5024, bigPicture = 0, extender = 0, - style = "MessagingStyle", + style = NotificationEnums.STYLE_MESSAGING, styleIcon = personIcon.bitmap.allocationByteCount + historicPersonIcon.bitmap.allocationByteCount, @@ -225,7 +246,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 3612, bigPicture = 0, extender = 556656, - style = null, + style = NotificationEnums.STYLE_NONE, styleIcon = 0, hasCustomView = false, ) @@ -246,7 +267,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras = 3820, bigPicture = 0, extender = 388 + wearBackground.allocationByteCount, - style = null, + style = NotificationEnums.STYLE_NONE, styleIcon = 0, hasCustomView = false, ) @@ -272,7 +293,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { extras: Int, bigPicture: Int, extender: Int, - style: String?, + style: Int, styleIcon: Int, hasCustomView: Boolean, ) { @@ -282,11 +303,7 @@ class NotificationMemoryMeterTest : SysuiTestCase() { assertThat(memoryUse.objectUsage.smallIcon).isEqualTo(smallIcon) assertThat(memoryUse.objectUsage.largeIcon).isEqualTo(largeIcon) assertThat(memoryUse.objectUsage.bigPicture).isEqualTo(bigPicture) - if (style == null) { - assertThat(memoryUse.objectUsage.style).isNull() - } else { - assertThat(memoryUse.objectUsage.style).isEqualTo(style) - } + assertThat(memoryUse.objectUsage.style).isEqualTo(style) assertThat(memoryUse.objectUsage.styleIcon).isEqualTo(styleIcon) assertThat(memoryUse.objectUsage.hasCustomView).isEqualTo(hasCustomView) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt index 3a16fb33388b..a0f50486ffff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt @@ -8,6 +8,7 @@ import android.testing.TestableLooper import android.widget.RemoteViews import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.tests.R import com.google.common.truth.Truth.assertThat @@ -39,16 +40,84 @@ class NotificationMemoryViewWalkerTest : SysuiTestCase() { fun testViewWalker_plainNotification() { val row = testHelper.createRow() val result = NotificationMemoryViewWalker.getViewUsage(row) - assertThat(result).hasSize(5) - assertThat(result).contains(NotificationViewUsage(ViewType.PUBLIC_VIEW, 0, 0, 0, 0, 0, 0)) - assertThat(result) - .contains(NotificationViewUsage(ViewType.PRIVATE_HEADS_UP_VIEW, 0, 0, 0, 0, 0, 0)) + assertThat(result).hasSize(3) assertThat(result) .contains(NotificationViewUsage(ViewType.PRIVATE_EXPANDED_VIEW, 0, 0, 0, 0, 0, 0)) assertThat(result) .contains(NotificationViewUsage(ViewType.PRIVATE_CONTRACTED_VIEW, 0, 0, 0, 0, 0, 0)) + assertThat(result).contains(NotificationViewUsage(ViewType.TOTAL, 0, 0, 0, 0, 0, 0)) + } + + @Test + fun testViewWalker_plainNotification_withPublicView() { + val icon = Icon.createWithBitmap(Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888)) + val publicIcon = Icon.createWithBitmap(Bitmap.createBitmap(40, 40, Bitmap.Config.ARGB_8888)) + testHelper.setDefaultInflationFlags(NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL) + val row = + testHelper.createRow( + Notification.Builder(mContext) + .setContentText("Test") + .setContentTitle("title") + .setSmallIcon(icon) + .setPublicVersion( + Notification.Builder(mContext) + .setContentText("Public Test") + .setContentTitle("title") + .setSmallIcon(publicIcon) + .build() + ) + .build() + ) + val result = NotificationMemoryViewWalker.getViewUsage(row) + assertThat(result).hasSize(4) assertThat(result) - .contains(NotificationViewUsage(ViewType.PRIVATE_HEADS_UP_VIEW, 0, 0, 0, 0, 0, 0)) + .contains( + NotificationViewUsage( + ViewType.PRIVATE_EXPANDED_VIEW, + icon.bitmap.allocationByteCount, + 0, + 0, + 0, + 0, + icon.bitmap.allocationByteCount + ) + ) + assertThat(result) + .contains( + NotificationViewUsage( + ViewType.PRIVATE_CONTRACTED_VIEW, + icon.bitmap.allocationByteCount, + 0, + 0, + 0, + 0, + icon.bitmap.allocationByteCount + ) + ) + assertThat(result) + .contains( + NotificationViewUsage( + ViewType.PUBLIC_VIEW, + publicIcon.bitmap.allocationByteCount, + 0, + 0, + 0, + 0, + publicIcon.bitmap.allocationByteCount + ) + ) + assertThat(result) + .contains( + NotificationViewUsage( + ViewType.TOTAL, + icon.bitmap.allocationByteCount + publicIcon.bitmap.allocationByteCount, + 0, + 0, + 0, + 0, + icon.bitmap.allocationByteCount + publicIcon.bitmap.allocationByteCount + ) + ) } @Test @@ -67,7 +136,7 @@ class NotificationMemoryViewWalkerTest : SysuiTestCase() { .build() ) val result = NotificationMemoryViewWalker.getViewUsage(row) - assertThat(result).hasSize(5) + assertThat(result).hasSize(3) assertThat(result) .contains( NotificationViewUsage( @@ -95,8 +164,20 @@ class NotificationMemoryViewWalkerTest : SysuiTestCase() { icon.bitmap.allocationByteCount + largeIcon.bitmap.allocationByteCount ) ) - // Due to deduplication, this should all be 0. - assertThat(result).contains(NotificationViewUsage(ViewType.PUBLIC_VIEW, 0, 0, 0, 0, 0, 0)) + assertThat(result) + .contains( + NotificationViewUsage( + ViewType.TOTAL, + icon.bitmap.allocationByteCount, + largeIcon.bitmap.allocationByteCount, + 0, + bigPicture.allocationByteCount, + 0, + bigPicture.allocationByteCount + + icon.bitmap.allocationByteCount + + largeIcon.bitmap.allocationByteCount + ) + ) } @Test @@ -117,7 +198,7 @@ class NotificationMemoryViewWalkerTest : SysuiTestCase() { .build() ) val result = NotificationMemoryViewWalker.getViewUsage(row) - assertThat(result).hasSize(5) + assertThat(result).hasSize(3) assertThat(result) .contains( NotificationViewUsage( @@ -142,7 +223,17 @@ class NotificationMemoryViewWalkerTest : SysuiTestCase() { bitmap.allocationByteCount + icon.bitmap.allocationByteCount ) ) - // Due to deduplication, this should all be 0. - assertThat(result).contains(NotificationViewUsage(ViewType.PUBLIC_VIEW, 0, 0, 0, 0, 0, 0)) + assertThat(result) + .contains( + NotificationViewUsage( + ViewType.TOTAL, + icon.bitmap.allocationByteCount, + 0, + 0, + 0, + bitmap.allocationByteCount, + bitmap.allocationByteCount + icon.bitmap.allocationByteCount + ) + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt index 5f5769572008..3f61af0425de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.FakeShadowView import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.SourceType import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -83,4 +84,17 @@ class ActivatableNotificationViewTest : SysuiTestCase() { mView.updateBackgroundColors() assertThat(mView.currentBackgroundTint).isEqualTo(mNormalColor) } + + @Test + fun roundnessShouldBeTheSame_after_onDensityOrFontScaleChanged() { + val roundableState = mView.roundableState + assertThat(mView.topRoundness).isEqualTo(0f) + mView.requestTopRoundness(1f, SourceType.from("")) + assertThat(mView.topRoundness).isEqualTo(1f) + + mView.onDensityOrFontScaleChanged() + + assertThat(mView.topRoundness).isEqualTo(1f) + assertThat(mView.roundableState.hashCode()).isEqualTo(roundableState.hashCode()) + } }
\ No newline at end of file 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 cae414a3dc67..19658e6398c1 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 @@ -55,6 +55,7 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -220,7 +221,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(NotificationPanelViewController.class), mActivityLaunchAnimator, notificationAnimationProvider, - mock(LaunchFullScreenIntentProvider.class) + mock(LaunchFullScreenIntentProvider.class), + mock(FeatureFlags.class) ); // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt index 22c0ea117b66..59c10cd6df7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt @@ -52,6 +52,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -234,6 +235,7 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() { } @Test + @Ignore("b/262660044") fun onDarkChanged_iconHasNewColor() { whenever(statusBarPipelineFlags.useWifiDebugColoring()).thenReturn(false) val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel) diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS new file mode 100644 index 000000000000..7ccb316dbca5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 1254381 +azappone@google.com +achalke@google.com +juliacr@google.com +madym@google.com +mgalhardo@google.com +petrcermak@google.com +vanjan@google.com
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt new file mode 100644 index 000000000000..ff382a3ec19f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt @@ -0,0 +1,158 @@ +/* + * 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.stylus + +import android.hardware.BatteryState +import android.hardware.input.InputManager +import android.testing.AndroidTestingRunner +import android.view.InputDevice +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.util.mockito.whenever +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.inOrder +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class StylusUsiPowerStartableTest : SysuiTestCase() { + @Mock lateinit var inputManager: InputManager + @Mock lateinit var stylusManager: StylusManager + @Mock lateinit var stylusDevice: InputDevice + @Mock lateinit var externalDevice: InputDevice + @Mock lateinit var featureFlags: FeatureFlags + @Mock lateinit var stylusUsiPowerUi: StylusUsiPowerUI + + lateinit var startable: StylusUsiPowerStartable + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + startable = + StylusUsiPowerStartable( + stylusManager, + inputManager, + stylusUsiPowerUi, + featureFlags, + DIRECT_EXECUTOR, + ) + + whenever(featureFlags.isEnabled(Flags.ENABLE_USI_BATTERY_NOTIFICATIONS)).thenReturn(true) + + whenever(inputManager.getInputDevice(EXTERNAL_DEVICE_ID)).thenReturn(externalDevice) + whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice) + whenever(inputManager.inputDeviceIds) + .thenReturn(intArrayOf(EXTERNAL_DEVICE_ID, STYLUS_DEVICE_ID)) + + whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) + whenever(stylusDevice.isExternal).thenReturn(false) + whenever(stylusDevice.id).thenReturn(STYLUS_DEVICE_ID) + whenever(externalDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) + whenever(externalDevice.isExternal).thenReturn(true) + whenever(externalDevice.id).thenReturn(EXTERNAL_DEVICE_ID) + } + + @Test + fun start_addsBatteryListenerForInternalStylus() { + startable.start() + + verify(inputManager, times(1)) + .addInputDeviceBatteryListener(STYLUS_DEVICE_ID, DIRECT_EXECUTOR, startable) + } + + @Test + fun onStylusAdded_internalStylus_addsBatteryListener() { + startable.onStylusAdded(STYLUS_DEVICE_ID) + + verify(inputManager, times(1)) + .addInputDeviceBatteryListener(STYLUS_DEVICE_ID, DIRECT_EXECUTOR, startable) + } + + @Test + fun onStylusAdded_externalStylus_doesNotAddBatteryListener() { + startable.onStylusAdded(EXTERNAL_DEVICE_ID) + + verify(inputManager, never()) + .addInputDeviceBatteryListener(EXTERNAL_DEVICE_ID, DIRECT_EXECUTOR, startable) + } + + @Test + fun onStylusRemoved_registeredStylus_removesBatteryListener() { + startable.onStylusAdded(STYLUS_DEVICE_ID) + startable.onStylusRemoved(STYLUS_DEVICE_ID) + + inOrder(inputManager).let { + it.verify(inputManager, times(1)) + .addInputDeviceBatteryListener(STYLUS_DEVICE_ID, DIRECT_EXECUTOR, startable) + it.verify(inputManager, times(1)) + .removeInputDeviceBatteryListener(STYLUS_DEVICE_ID, startable) + } + } + + @Test + fun onStylusBluetoothConnected_refreshesNotification() { + startable.onStylusBluetoothConnected(STYLUS_DEVICE_ID, "ANY") + + verify(stylusUsiPowerUi, times(1)).refresh() + } + + @Test + fun onStylusBluetoothDisconnected_refreshesNotification() { + startable.onStylusBluetoothDisconnected(STYLUS_DEVICE_ID, "ANY") + + verify(stylusUsiPowerUi, times(1)).refresh() + } + + @Test + fun onBatteryStateChanged_batteryPresent_refreshesNotification() { + val batteryState = mock(BatteryState::class.java) + whenever(batteryState.isPresent).thenReturn(true) + + startable.onBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState) + + verify(stylusUsiPowerUi, times(1)).updateBatteryState(batteryState) + } + + @Test + fun onBatteryStateChanged_batteryNotPresent_noop() { + val batteryState = mock(BatteryState::class.java) + whenever(batteryState.isPresent).thenReturn(false) + + startable.onBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState) + + verifyNoMoreInteractions(stylusUsiPowerUi) + } + + companion object { + private val DIRECT_EXECUTOR = Executor { r -> r.run() } + + private const val EXTERNAL_DEVICE_ID = 0 + private const val STYLUS_DEVICE_ID = 1 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt new file mode 100644 index 000000000000..59875507341d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt @@ -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.systemui.stylus + +import android.hardware.BatteryState +import android.hardware.input.InputManager +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.view.InputDevice +import androidx.core.app.NotificationManagerCompat +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.inOrder +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class StylusUsiPowerUiTest : SysuiTestCase() { + @Mock lateinit var notificationManager: NotificationManagerCompat + @Mock lateinit var inputManager: InputManager + @Mock lateinit var handler: Handler + @Mock lateinit var btStylusDevice: InputDevice + + private lateinit var stylusUsiPowerUi: StylusUsiPowerUI + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(handler.post(any())).thenAnswer { + (it.arguments[0] as Runnable).run() + true + } + + whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf()) + whenever(inputManager.getInputDevice(0)).thenReturn(btStylusDevice) + whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) + // whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES") + + stylusUsiPowerUi = StylusUsiPowerUI(mContext, notificationManager, inputManager, handler) + } + + @Test + fun updateBatteryState_capacityBelowThreshold_notifies() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + + verify(notificationManager, times(1)).notify(eq(R.string.stylus_battery_low), any()) + verifyNoMoreInteractions(notificationManager) + } + + @Test + fun updateBatteryState_capacityAboveThreshold_cancelsNotificattion() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.8f)) + + verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low) + verifyNoMoreInteractions(notificationManager) + } + + @Test + fun updateBatteryState_existingNotification_capacityAboveThreshold_cancelsNotification() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.8f)) + + inOrder(notificationManager).let { + it.verify(notificationManager, times(1)).notify(eq(R.string.stylus_battery_low), any()) + it.verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low) + it.verifyNoMoreInteractions() + } + } + + @Test + fun updateBatteryState_existingNotification_capacityBelowThreshold_updatesNotification() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.15f)) + + verify(notificationManager, times(2)).notify(eq(R.string.stylus_battery_low), any()) + verifyNoMoreInteractions(notificationManager) + } + + @Test + fun updateBatteryState_capacityAboveThenBelowThreshold_hidesThenShowsNotification() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.5f)) + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + + inOrder(notificationManager).let { + it.verify(notificationManager, times(1)).notify(eq(R.string.stylus_battery_low), any()) + it.verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low) + it.verify(notificationManager, times(1)).notify(eq(R.string.stylus_battery_low), any()) + it.verifyNoMoreInteractions() + } + } + + @Test + fun updateSuppression_noExistingNotification_cancelsNotification() { + stylusUsiPowerUi.updateSuppression(true) + + verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low) + verifyNoMoreInteractions(notificationManager) + } + + @Test + fun updateSuppression_existingNotification_cancelsNotification() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + + stylusUsiPowerUi.updateSuppression(true) + + inOrder(notificationManager).let { + it.verify(notificationManager, times(1)).notify(eq(R.string.stylus_battery_low), any()) + it.verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low) + it.verifyNoMoreInteractions() + } + } + + @Test + @Ignore("TODO(b/257936830): get bt address once input api available") + fun refresh_hasConnectedBluetoothStylus_doesNotNotify() { + whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0)) + + stylusUsiPowerUi.refresh() + + verifyNoMoreInteractions(notificationManager) + } + + @Test + @Ignore("TODO(b/257936830): get bt address once input api available") + fun refresh_hasConnectedBluetoothStylus_existingNotification_cancelsNotification() { + stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f)) + whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0)) + + stylusUsiPowerUi.refresh() + + verify(notificationManager).cancel(R.string.stylus_battery_low) + } + + class FixedCapacityBatteryState(private val capacity: Float) : BatteryState() { + override fun getCapacity() = capacity + override fun getStatus() = 0 + override fun isPresent() = true + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 5c2a915e81b6..55019490bdcd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -120,6 +120,14 @@ class FakeKeyguardRepository : KeyguardRepository { _dozeAmount.value = dozeAmount } + fun setWakefulnessModel(model: WakefulnessModel) { + _wakefulnessModel.value = model + } + + fun setBouncerShowing(isShowing: Boolean) { + _isBouncerShowing.value = isShowing + } + fun setBiometricUnlockState(state: BiometricUnlockModel) { _biometricUnlockState.tryEmit(state) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt new file mode 100644 index 000000000000..2c0a8fde0d77 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.shade.data.repository + +import com.android.systemui.shade.domain.model.ShadeModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +/** Fake implementation of [KeyguardRepository] */ +class FakeShadeRepository : ShadeRepository { + + private val _shadeModel = MutableStateFlow(ShadeModel()) + override val shadeModel: Flow<ShadeModel> = _shadeModel + + fun setShadeModel(model: ShadeModel) { + _shadeModel.value = model + } +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 5c11e2c2327e..60890d529ef2 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -24,6 +24,7 @@ import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU; import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; +import static android.service.autofill.FillRequest.FLAG_RESET_FILL_DIALOG_STATE; import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; @@ -411,6 +412,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private boolean mStartedLogEventWithoutFocus; + /** + * Keeps the fill dialog trigger ids of the last response. This invalidates + * the trigger ids of the previous response. + */ + @Nullable + @GuardedBy("mLock") + private AutofillId[] mLastFillDialogTriggerIds; + void onSwitchInputMethodLocked() { // One caveat is that for the case where the focus is on a field for which regular autofill // returns null, and augmented autofill is triggered, and then the user switches the input @@ -1171,6 +1180,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + mLastFillDialogTriggerIds = response.getFillDialogTriggerIds(); + final int flags = response.getFlags(); if ((flags & FillResponse.FLAG_DELAY_FILL) != 0) { Slog.v(TAG, "Service requested to wait for delayed fill response."); @@ -1277,6 +1288,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + (timedOut ? "timeout" : "failure")); } mService.resetLastResponse(); + mLastFillDialogTriggerIds = null; final LogMaker requestLog = mRequestLogs.get(requestId); if (requestLog == null) { Slog.w(TAG, "onFillRequestFailureOrTimeout(): no log for id " + requestId); @@ -2973,6 +2985,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + if ((flags & FLAG_RESET_FILL_DIALOG_STATE) != 0) { + if (sDebug) Log.d(TAG, "force to reset fill dialog state"); + mSessionFlags.mFillDialogDisabled = false; + } + switch(action) { case ACTION_START_SESSION: // View is triggering autofill. @@ -3402,10 +3419,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private boolean isFillDialogUiEnabled() { - // TODO read from Settings or somewhere - final boolean isSettingsEnabledFillDialog = true; synchronized (mLock) { - return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled; + return !mSessionFlags.mFillDialogDisabled; } } @@ -3431,14 +3446,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutofillId filledId, String filterText, int flags) { if (!isFillDialogUiEnabled()) { // Unsupported fill dialog UI + if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled"); return false; } if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) { // IME is showing, fallback to normal suggestions UI + if (sDebug) Log.w(TAG, "requestShowFillDialog: IME is showing"); return false; } + synchronized (mLock) { + if (mLastFillDialogTriggerIds == null + || !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) { + // Last fill dialog triggered ids are changed. + if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed."); + return false; + } + } + final Drawable serviceIcon = getServiceIcon(); getUiForShowing().showFillDialog(filledId, response, filterText, @@ -4278,6 +4304,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mSessionFlags.mAugmentedAutofillOnly) { pw.print(prefix); pw.println("For Augmented Autofill Only"); } + if (mSessionFlags.mFillDialogDisabled) { + pw.print(prefix); pw.println("Fill Dialog disabled"); + } + if (mLastFillDialogTriggerIds != null) { + pw.print(prefix); pw.println("Last Fill Dialog trigger ids: "); + pw.println(mSelectedDatasetIds); + } if (mAugmentedAutofillDestroyer != null) { pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer"); } diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index e529010e6b7d..daba4c0e83a0 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -79,7 +79,7 @@ public class GestureLauncherService extends SystemService { * completed faster than this, we assume it's not performed by human and the * event gets ignored. */ - @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 160; + @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 200; /** * Interval in milliseconds in which the power button must be depressed in succession to be diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index bea0f4ffd45d..846c2d9f3df7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -417,7 +417,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } @Override - public void onTrustChanged(boolean enabled, int userId, int flags, + public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags, List<String> trustGrantedMessages) { mUserHasTrust.put(userId, enabled); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 2be2d584f9a3..401d184dd7fb 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -36,16 +36,19 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; import com.android.server.display.config.AutoBrightness; +import com.android.server.display.config.BlockingZoneConfig; import com.android.server.display.config.BrightnessThresholds; import com.android.server.display.config.BrightnessThrottlingMap; import com.android.server.display.config.BrightnessThrottlingPoint; import com.android.server.display.config.Density; +import com.android.server.display.config.DisplayBrightnessPoint; import com.android.server.display.config.DisplayConfiguration; import com.android.server.display.config.DisplayQuirks; import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; +import com.android.server.display.config.RefreshRateConfigs; import com.android.server.display.config.RefreshRateRange; import com.android.server.display.config.SdrHdrRatioMap; import com.android.server.display.config.SdrHdrRatioPoint; @@ -130,6 +133,35 @@ import javax.xml.datatype.DatatypeConfigurationException; * </brightnessThrottlingMap> * </thermalThrottling> * + * <refreshRate> + * <lowerBlockingZoneConfigs> + * <defaultRefreshRate>75</defaultRefreshRate> + * <blockingZoneThreshold> + * <displayBrightnessPoint> + * <lux>50</lux> + * <nits>45.3</nits> + * </displayBrightnessPoint> + * <displayBrightnessPoint> + * <lux>60</lux> + * <nits>55.2</nits> + * </displayBrightnessPoint> + * </blockingZoneThreshold> + * </lowerBlockingZoneConfigs> + * <higherBlockingZoneConfigs> + * <defaultRefreshRate>90</defaultRefreshRate> + * <blockingZoneThreshold> + * <displayBrightnessPoint> + * <lux>500</lux> + * <nits>245.3</nits> + * </displayBrightnessPoint> + * <displayBrightnessPoint> + * <lux>600</lux> + * <nits>232.3</nits> + * </displayBrightnessPoint> + * </blockingZoneThreshold> + * </higherBlockingZoneConfigs> + * </refreshRate> + * * <highBrightnessMode enabled="true"> * <transitionPoint>0.62</transitionPoint> * <minimumLux>10000</minimumLux> @@ -358,6 +390,9 @@ public class DisplayDeviceConfig { private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d"; private static final String NO_SUFFIX_FORMAT = "%d"; private static final long STABLE_FLAG = 1L << 62; + private static final int DEFAULT_LOW_REFRESH_RATE = 60; + private static final int DEFAULT_HIGH_REFRESH_RATE = 0; + private static final int[] DEFAULT_BRIGHTNESS_THRESHOLDS = new int[]{}; private static final float[] DEFAULT_AMBIENT_THRESHOLD_LEVELS = new float[]{0f}; private static final float[] DEFAULT_AMBIENT_BRIGHTENING_THRESHOLDS = new float[]{100f}; @@ -512,6 +547,49 @@ public class DisplayDeviceConfig { // This stores the raw value loaded from the config file - true if not written. private boolean mDdcAutoBrightnessAvailable = true; + /** + * The default peak refresh rate for a given device. This value prevents the framework from + * using higher refresh rates, even if display modes with higher refresh rates are available + * from hardware composer. Only has an effect if the value is non-zero. + */ + private int mDefaultHighRefreshRate = DEFAULT_HIGH_REFRESH_RATE; + + /** + * The default refresh rate for a given device. This value sets the higher default + * refresh rate. If the hardware composer on the device supports display modes with + * a higher refresh rate than the default value specified here, the framework may use those + * higher refresh rate modes if an app chooses one by setting preferredDisplayModeId or calling + * setFrameRate(). We have historically allowed fallback to mDefaultHighRefreshRate if + * mDefaultLowRefreshRate is set to 0, but this is not supported anymore. + */ + private int mDefaultLowRefreshRate = DEFAULT_LOW_REFRESH_RATE; + + /** + * The display uses different gamma curves for different refresh rates. It's hard for panel + * vendors to tune the curves to have exact same brightness for different refresh rate. So + * brightness flickers could be observed at switch time. The issue is worse at the gamma lower + * end. In addition, human eyes are more sensitive to the flicker at darker environment. To + * prevent flicker, we only support higher refresh rates if the display brightness is above a + * threshold. For example, no higher refresh rate if display brightness <= disp0 && ambient + * brightness <= amb0 || display brightness <= disp1 && ambient brightness <= amb1 + */ + private int[] mLowDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; + private int[] mLowAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; + + /** + * The display uses different gamma curves for different refresh rates. It's hard for panel + * vendors to tune the curves to have exact same brightness for different refresh rate. So + * brightness flickers could be observed at switch time. The issue can be observed on the screen + * with even full white content at the high brightness. To prevent flickering, we support fixed + * refresh rates if the display and ambient brightness are equal to or above the provided + * thresholds. You can define multiple threshold levels as higher brightness environments may + * have lower display brightness requirements for the flickering is visible. For example, fixed + * refresh rate if display brightness >= disp0 && ambient brightness >= amb0 || display + * brightness >= disp1 && ambient brightness >= amb1 + */ + private int[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; + private int[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; + // Brightness Throttling data may be updated via the DeviceConfig. Here we store the original // data, which comes from the ddc, and the current one, which may be the DeviceConfig // overwritten value. @@ -1196,15 +1274,15 @@ public class DisplayDeviceConfig { /** * @return Default peak refresh rate of the associated display */ - public int getDefaultPeakRefreshRate() { - return mContext.getResources().getInteger(R.integer.config_defaultPeakRefreshRate); + public int getDefaultHighRefreshRate() { + return mDefaultHighRefreshRate; } /** * @return Default refresh rate of the associated display */ - public int getDefaultRefreshRate() { - return mContext.getResources().getInteger(R.integer.config_defaultRefreshRate); + public int getDefaultLowRefreshRate() { + return mDefaultLowRefreshRate; } /** @@ -1213,8 +1291,7 @@ public class DisplayDeviceConfig { * allowed */ public int[] getLowDisplayBrightnessThresholds() { - return mContext.getResources().getIntArray( - R.array.config_brightnessThresholdsOfPeakRefreshRate); + return mLowDisplayBrightnessThresholds; } /** @@ -1223,8 +1300,7 @@ public class DisplayDeviceConfig { * allowed */ public int[] getLowAmbientBrightnessThresholds() { - return mContext.getResources().getIntArray( - R.array.config_ambientThresholdsOfPeakRefreshRate); + return mLowAmbientBrightnessThresholds; } /** @@ -1233,8 +1309,7 @@ public class DisplayDeviceConfig { * allowed */ public int[] getHighDisplayBrightnessThresholds() { - return mContext.getResources().getIntArray( - R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); + return mHighDisplayBrightnessThresholds; } /** @@ -1243,8 +1318,7 @@ public class DisplayDeviceConfig { * allowed */ public int[] getHighAmbientBrightnessThresholds() { - return mContext.getResources().getIntArray( - R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + return mHighAmbientBrightnessThresholds; } @Override @@ -1336,6 +1410,17 @@ public class DisplayDeviceConfig { + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits) + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable + + "\n" + + ", mDefaultRefreshRate= " + mDefaultLowRefreshRate + + ", mDefaultPeakRefreshRate= " + mDefaultHighRefreshRate + + ", mLowDisplayBrightnessThresholds= " + + Arrays.toString(mLowDisplayBrightnessThresholds) + + ", mLowAmbientBrightnessThresholds= " + + Arrays.toString(mLowAmbientBrightnessThresholds) + + ", mHighDisplayBrightnessThresholds= " + + Arrays.toString(mHighDisplayBrightnessThresholds) + + ", mHighAmbientBrightnessThresholds= " + + Arrays.toString(mHighAmbientBrightnessThresholds) + "}"; } @@ -1393,6 +1478,7 @@ public class DisplayDeviceConfig { loadAmbientHorizonFromDdc(config); loadBrightnessChangeThresholds(config); loadAutoBrightnessConfigValues(config); + loadRefreshRateSetting(config); } else { Slog.w(TAG, "DisplayDeviceConfig file is null"); } @@ -1415,6 +1501,7 @@ public class DisplayDeviceConfig { useFallbackProxSensor(); loadAutoBrightnessConfigsFromConfigXml(); loadAutoBrightnessAvailableFromConfigXml(); + loadRefreshRateSetting(null); mLoadedFrom = "<config.xml>"; } @@ -1625,6 +1712,143 @@ public class DisplayDeviceConfig { } } + private void loadRefreshRateSetting(DisplayConfiguration config) { + final RefreshRateConfigs refreshRateConfigs = + (config == null) ? null : config.getRefreshRate(); + BlockingZoneConfig lowerBlockingZoneConfig = + (refreshRateConfigs == null) ? null + : refreshRateConfigs.getLowerBlockingZoneConfigs(); + BlockingZoneConfig higherBlockingZoneConfig = + (refreshRateConfigs == null) ? null + : refreshRateConfigs.getHigherBlockingZoneConfigs(); + loadLowerRefreshRateBlockingZones(lowerBlockingZoneConfig); + loadHigherRefreshRateBlockingZones(higherBlockingZoneConfig); + } + + + /** + * Loads the refresh rate configurations pertaining to the upper blocking zones. + */ + private void loadLowerRefreshRateBlockingZones(BlockingZoneConfig lowerBlockingZoneConfig) { + loadLowerBlockingZoneDefaultRefreshRate(lowerBlockingZoneConfig); + loadLowerBrightnessThresholds(lowerBlockingZoneConfig); + } + + /** + * Loads the refresh rate configurations pertaining to the upper blocking zones. + */ + private void loadHigherRefreshRateBlockingZones(BlockingZoneConfig upperBlockingZoneConfig) { + loadHigherBlockingZoneDefaultRefreshRate(upperBlockingZoneConfig); + loadHigherBrightnessThresholds(upperBlockingZoneConfig); + } + + /** + * Loads the default peak refresh rate. Internally, this takes care of loading + * the value from the display config, and if not present, falls back to config.xml. + */ + private void loadHigherBlockingZoneDefaultRefreshRate( + BlockingZoneConfig upperBlockingZoneConfig) { + if (upperBlockingZoneConfig == null) { + mDefaultHighRefreshRate = mContext.getResources().getInteger( + com.android.internal.R.integer.config_defaultPeakRefreshRate); + } else { + mDefaultHighRefreshRate = + upperBlockingZoneConfig.getDefaultRefreshRate().intValue(); + } + } + + /** + * Loads the default refresh rate. Internally, this takes care of loading + * the value from the display config, and if not present, falls back to config.xml. + */ + private void loadLowerBlockingZoneDefaultRefreshRate( + BlockingZoneConfig lowerBlockingZoneConfig) { + if (lowerBlockingZoneConfig == null) { + mDefaultLowRefreshRate = mContext.getResources().getInteger( + com.android.internal.R.integer.config_defaultRefreshRate); + } else { + mDefaultLowRefreshRate = + lowerBlockingZoneConfig.getDefaultRefreshRate().intValue(); + } + } + + /** + * Loads the lower brightness thresholds for refresh rate switching. Internally, this takes care + * of loading the value from the display config, and if not present, falls back to config.xml. + */ + private void loadLowerBrightnessThresholds(BlockingZoneConfig lowerBlockingZoneConfig) { + if (lowerBlockingZoneConfig == null) { + mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_brightnessThresholdsOfPeakRefreshRate); + mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_ambientThresholdsOfPeakRefreshRate); + if (mLowDisplayBrightnessThresholds == null || mLowAmbientBrightnessThresholds == null + || mLowDisplayBrightnessThresholds.length + != mLowAmbientBrightnessThresholds.length) { + throw new RuntimeException("display low brightness threshold array and ambient " + + "brightness threshold array have different length: " + + "mLowDisplayBrightnessThresholds=" + + Arrays.toString(mLowDisplayBrightnessThresholds) + + ", mLowAmbientBrightnessThresholds=" + + Arrays.toString(mLowAmbientBrightnessThresholds)); + } + } else { + List<DisplayBrightnessPoint> lowerThresholdDisplayBrightnessPoints = + lowerBlockingZoneConfig.getBlockingZoneThreshold().getDisplayBrightnessPoint(); + int size = lowerThresholdDisplayBrightnessPoints.size(); + mLowDisplayBrightnessThresholds = new int[size]; + mLowAmbientBrightnessThresholds = new int[size]; + for (int i = 0; i < size; i++) { + // We are explicitly casting this value to an integer to be able to reuse the + // existing DisplayBrightnessPoint type. It is fine to do this because the round off + // will have the negligible and unnoticeable impact on the loaded thresholds. + mLowDisplayBrightnessThresholds[i] = (int) lowerThresholdDisplayBrightnessPoints + .get(i).getNits().floatValue(); + mLowAmbientBrightnessThresholds[i] = lowerThresholdDisplayBrightnessPoints + .get(i).getLux().intValue(); + } + } + } + + /** + * Loads the higher brightness thresholds for refresh rate switching. Internally, this takes + * care of loading the value from the display config, and if not present, falls back to + * config.xml. + */ + private void loadHigherBrightnessThresholds(BlockingZoneConfig blockingZoneConfig) { + if (blockingZoneConfig == null) { + mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); + mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + if (mHighAmbientBrightnessThresholds == null || mHighDisplayBrightnessThresholds == null + || mHighAmbientBrightnessThresholds.length + != mHighDisplayBrightnessThresholds.length) { + throw new RuntimeException("display high brightness threshold array and ambient " + + "brightness threshold array have different length: " + + "mHighDisplayBrightnessThresholds=" + + Arrays.toString(mHighDisplayBrightnessThresholds) + + ", mHighAmbientBrightnessThresholds=" + + Arrays.toString(mHighAmbientBrightnessThresholds)); + } + } else { + List<DisplayBrightnessPoint> higherThresholdDisplayBrightnessPoints = + blockingZoneConfig.getBlockingZoneThreshold().getDisplayBrightnessPoint(); + int size = higherThresholdDisplayBrightnessPoints.size(); + mHighDisplayBrightnessThresholds = new int[size]; + mHighAmbientBrightnessThresholds = new int[size]; + for (int i = 0; i < size; i++) { + // We are explicitly casting this value to an integer to be able to reuse the + // existing DisplayBrightnessPoint type. It is fine to do this because the round off + // will have the negligible and unnoticeable impact on the loaded thresholds. + mHighDisplayBrightnessThresholds[i] = (int) higherThresholdDisplayBrightnessPoints + .get(i).getNits().floatValue(); + mHighAmbientBrightnessThresholds[i] = higherThresholdDisplayBrightnessPoints + .get(i).getLux().intValue(); + } + } + } + private void loadAutoBrightnessConfigValues(DisplayConfiguration config) { final AutoBrightness autoBrightness = config.getAutoBrightness(); loadAutoBrightnessBrighteningLightDebounce(autoBrightness); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index f9c8f064de96..3da7d830a23f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -3652,44 +3652,21 @@ public final class DisplayManagerService extends SystemService { @Override public Set<DisplayInfo> getPossibleDisplayInfo(int displayId) { synchronized (mSyncRoot) { - // Retrieve the group associated with this display id. - final int displayGroupId = - mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(displayId); - if (displayGroupId == Display.INVALID_DISPLAY_GROUP) { - Slog.w(TAG, - "Can't get possible display info since display group for " + displayId - + " does not exist"); - return new ArraySet<>(); - } - - // Assume any display in this group can be swapped out for the given display id. Set<DisplayInfo> possibleInfo = new ArraySet<>(); - final DisplayGroup group = mLogicalDisplayMapper.getDisplayGroupLocked( - displayGroupId); - for (int i = 0; i < group.getSizeLocked(); i++) { - final int id = group.getIdLocked(i); - final LogicalDisplay logical = mLogicalDisplayMapper.getDisplayLocked(id); - if (logical == null) { - Slog.w(TAG, - "Can't get possible display info since logical display for " - + "display id " + id + " does not exist, as part of group " - + displayGroupId); - } else { - possibleInfo.add(logical.getDisplayInfoLocked()); - } - } - - // For the supported device states, retrieve the DisplayInfos for the logical - // display layout. + // For each of supported device states, retrieve the display layout of that state, + // and return all of the DisplayInfos (one per state) for the given display id. if (mDeviceStateManager == null) { Slog.w(TAG, "Can't get supported states since DeviceStateManager not ready"); - } else { - final int[] supportedStates = - mDeviceStateManager.getSupportedStateIdentifiers(); - for (int state : supportedStates) { - possibleInfo.addAll( - mLogicalDisplayMapper.getDisplayInfoForStateLocked(state, displayId, - displayGroupId)); + return possibleInfo; + } + final int[] supportedStates = + mDeviceStateManager.getSupportedStateIdentifiers(); + DisplayInfo displayInfo; + for (int state : supportedStates) { + displayInfo = mLogicalDisplayMapper.getDisplayInfoForStateLocked(state, + displayId); + if (displayInfo != null) { + possibleInfo.add(displayInfo); } } return possibleInfo; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index ecae8330d532..6331a5dd07b4 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -1163,7 +1163,7 @@ public class DisplayModeDirector { mDefaultRefreshRate = (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger( R.integer.config_defaultRefreshRate) - : (float) displayDeviceConfig.getDefaultRefreshRate(); + : (float) displayDeviceConfig.getDefaultLowRefreshRate(); } public void observe() { @@ -1250,7 +1250,7 @@ public class DisplayModeDirector { defaultPeakRefreshRate = (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger( R.integer.config_defaultPeakRefreshRate) - : (float) displayDeviceConfig.getDefaultPeakRefreshRate(); + : (float) displayDeviceConfig.getDefaultHighRefreshRate(); } mDefaultPeakRefreshRate = defaultPeakRefreshRate; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index bb27651ed0ed..2fc6fd2254a7 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -459,6 +459,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set. private float mTemporaryAutoBrightnessAdjustment; + private boolean mUseAutoBrightness; + private boolean mIsRbcActive; // Whether there's a callback to tell listeners the display has changed scheduled to run. When @@ -692,6 +694,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void onSwitchUser(@UserIdInt int newUserId) { handleSettingsChange(true /* userSwitch */); + handleBrightnessModeChange(); if (mBrightnessTracker != null) { mBrightnessTracker.onSwitchUser(newUserId); } @@ -941,6 +944,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), + false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); + handleBrightnessModeChange(); } private void setUpAutoBrightness(Resources resources, Handler handler) { @@ -1344,11 +1351,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); - final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness + final boolean autoBrightnessEnabled = mUseAutoBrightness && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) && Float.isNaN(brightnessState) && mAutomaticBrightnessController != null; - final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness + final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); final int autoBrightnessState = autoBrightnessEnabled ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED @@ -1700,7 +1707,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call || brightnessAdjustmentFlags != 0) { float lastBrightness = mLastBrightnessEvent.brightness; mTempBrightnessEvent.initialBrightness = lastBrightness; - mTempBrightnessEvent.automaticBrightnessEnabled = mPowerRequest.useAutoBrightness; + mTempBrightnessEvent.automaticBrightnessEnabled = mUseAutoBrightness; mLastBrightnessEvent.copyFrom(mTempBrightnessEvent); BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent); @@ -2372,6 +2379,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call sendUpdatePowerState(); } + private void handleBrightnessModeChange() { + final int screenBrightnessModeSetting = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); + mHandler.post(() -> { + mUseAutoBrightness = screenBrightnessModeSetting + == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; + updatePowerState(); + }); + } + private float getAutoBrightnessAdjustmentSetting() { final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT); @@ -2461,7 +2480,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, boolean hadUserDataPoint) { final float brightnessInNits = convertToNits(brightness); - if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f + if (mUseAutoBrightness && brightnessInNits >= 0.0f && mAutomaticBrightnessController != null && mBrightnessTracker != null) { // We only want to track changes on devices that can actually map the display backlight // values into a physical brightness unit since the value provided by the API is in @@ -3099,7 +3118,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void onChange(boolean selfChange, Uri uri) { - handleSettingsChange(false /* userSwitch */); + if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) { + handleBrightnessModeChange(); + } else { + handleSettingsChange(false /* userSwitch */); + } } } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 778e41820433..bf576b8909f2 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -19,6 +19,7 @@ package com.android.server.display; import static android.view.Display.DEFAULT_DISPLAY; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.devicestate.DeviceStateManager; import android.os.Handler; @@ -28,7 +29,6 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; -import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; @@ -43,7 +43,6 @@ import com.android.server.display.layout.Layout; import java.io.PrintWriter; import java.util.Arrays; -import java.util.Set; import java.util.function.Consumer; /** @@ -304,58 +303,44 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } /** - * Returns the set of {@link DisplayInfo} for this device state, only fetching the info that is - * part of the same display group as the provided display id. The DisplayInfo represent the - * logical display layouts possible for the given device state. + * Returns the {@link DisplayInfo} for this device state, indicated by the given display id. The + * DisplayInfo represents the attributes of the indicated display in the layout associated with + * this state. This is used to get display information for various displays in various states; + * e.g. to help apps preload resources for the possible display states. * * @param deviceState the state to query possible layouts for - * @param displayId the display id to apply to all displays within the group - * @param groupId the display group to filter display info for. Must be the same group as - * the display with the provided display id. + * @param displayId the display id to retrieve + * @return {@code null} if no corresponding {@link DisplayInfo} could be found, or the + * {@link DisplayInfo} with a matching display id. */ - public Set<DisplayInfo> getDisplayInfoForStateLocked(int deviceState, int displayId, - int groupId) { - Set<DisplayInfo> displayInfos = new ArraySet<>(); + @Nullable + public DisplayInfo getDisplayInfoForStateLocked(int deviceState, int displayId) { + // Retrieve the layout for this particular state. final Layout layout = mDeviceStateToLayoutMap.get(deviceState); - final int layoutSize = layout.size(); - for (int i = 0; i < layoutSize; i++) { - Layout.Display displayLayout = layout.getAt(i); - if (displayLayout == null) { - continue; - } - - // If the underlying display-device we want to use for this display - // doesn't exist, then skip it. This can happen at startup as display-devices - // trickle in one at a time. When the new display finally shows up, the layout is - // recalculated so that the display is properly added to the current layout. - final DisplayAddress address = displayLayout.getAddress(); - final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address); - if (device == null) { - Slog.w(TAG, "The display device (" + address + "), is not available" - + " for the display state " + deviceState); - continue; - } - - // Find or create the LogicalDisplay to map the DisplayDevice to. - final int logicalDisplayId = displayLayout.getLogicalDisplayId(); - final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId); - if (logicalDisplay == null) { - Slog.w(TAG, "The logical display (" + address + "), is not available" - + " for the display state " + deviceState); - continue; - } - final DisplayInfo temp = logicalDisplay.getDisplayInfoLocked(); - DisplayInfo displayInfo = new DisplayInfo(temp); - if (displayInfo.displayGroupId != groupId) { - // Ignore any displays not in the provided group. - continue; - } - // A display in the same group can be swapped out at any point, so set the display id - // for all results to the provided display id. - displayInfo.displayId = displayId; - displayInfos.add(displayInfo); + if (layout == null) { + return null; + } + // Retrieve the details of the given display within this layout. + Layout.Display display = layout.getById(displayId); + if (display == null) { + return null; + } + // Retrieve the display info for the display that matches the display id. + final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(display.getAddress()); + if (device == null) { + Slog.w(TAG, "The display device (" + display.getAddress() + "), is not available" + + " for the display state " + mDeviceState); + return null; + } + LogicalDisplay logicalDisplay = getDisplayLocked(device, /* includeDisabled= */ true); + if (logicalDisplay == null) { + Slog.w(TAG, "The logical display associated with address (" + display.getAddress() + + "), is not available for the display state " + mDeviceState); + return null; } - return displayInfos; + DisplayInfo displayInfo = new DisplayInfo(logicalDisplay.getDisplayInfoLocked()); + displayInfo.displayId = displayId; + return displayInfo; } public void dumpLocked(PrintWriter pw) { @@ -426,7 +411,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // Send the device to sleep when required. mHandler.post(() -> { mPowerManager.goToSleep(SystemClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, 0); + PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, + PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP); }); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 1ea949ede03d..2f818fa10b72 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -123,6 +123,7 @@ import static android.telephony.CarrierConfigManager.KEY_DATA_RAPID_NOTIFICATION import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_NOTIFICATION_BOOL; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -3148,7 +3149,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * active merge set [A,B], we'd return a new template that primarily matches * A, but also matches B. */ - private static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template, + @VisibleForTesting(visibility = PRIVATE) + static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template, @NonNull List<String[]> mergedList) { // Now there are several types of network which uses Subscriber Id to store network // information. For instance: @@ -3158,6 +3160,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (template.getSubscriberIds().isEmpty()) return template; for (final String[] merged : mergedList) { + // In some rare cases (e.g. b/243015487), merged subscriberId list might contain + // duplicated items. Deduplication for better error handling. + final ArraySet mergedSet = new ArraySet(merged); + if (mergedSet.size() != merged.length) { + Log.wtf(TAG, "Duplicated merged list detected: " + Arrays.toString(merged)); + } // TODO: Handle incompatible subscriberIds if that happens in practice. for (final String subscriberId : template.getSubscriberIds()) { if (com.android.net.module.util.CollectionUtils.contains(merged, subscriberId)) { @@ -3165,7 +3173,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // a template that matches all merged subscribers. return new NetworkTemplate.Builder(template.getMatchRule()) .setWifiNetworkKeys(template.getWifiNetworkKeys()) - .setSubscriberIds(Set.of(merged)) + .setSubscriberIds(mergedSet) .setMeteredness(template.getMeteredness()) .build(); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 85a2a5d0e0eb..dbe049a8e18e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1049,12 +1049,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } - // Make sure the device locks. Unfortunately, this has the side-effect of briefly revealing - // the lock screen before the dream appears. Note that this locking behavior needs to - // happen regardless of whether we end up dreaming (below) or not. - // TODO(b/261662912): Find a better way to lock the device that doesn't result in jank. - lockNow(null); - // Don't dream if the user isn't user zero. // TODO(b/261907079): Move this check to DreamManagerService#canStartDreamingInternal(). if (ActivityManager.getCurrentUser() != UserHandle.USER_SYSTEM) { @@ -1068,6 +1062,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } + // Make sure the device locks. Unfortunately, this has the side-effect of briefly revealing + // the lock screen before the dream appears. Note that locking is a side-effect of the no + // dream action that is executed if we early return above. + // TODO(b/261662912): Find a better way to lock the device that doesn't result in jank. + lockNow(null); + dreamManagerInternal.requestDream(); } diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java index 431cf3861804..3a3f6b2b849d 100644 --- a/services/core/java/com/android/server/power/PowerGroup.java +++ b/services/core/java/com/android/server/power/PowerGroup.java @@ -28,6 +28,8 @@ import static com.android.server.power.PowerManagerService.USER_ACTIVITY_SCREEN_ import static com.android.server.power.PowerManagerService.WAKE_LOCK_DOZE; import static com.android.server.power.PowerManagerService.WAKE_LOCK_DRAW; import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_BRIGHT; +import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_DIM; +import static com.android.server.power.PowerManagerService.WAKE_LOCK_STAY_AWAKE; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; @@ -341,6 +343,22 @@ public class PowerGroup { return mWakeLockSummary; } + /** + * Query whether a wake lock is at least partially responsible for keeping the device awake. + * + * This does not necessarily mean the wake lock is the sole reason the device is awake; there + * could also be user activity keeping the device awake, for example. It just means a wake lock + * is being held that would keep the device awake even if nothing else was. + * + * @return whether the PowerGroup is being kept awake at least in part because a wake lock is + * being held. + */ + public boolean hasWakeLockKeepingScreenOnLocked() { + final int screenOnWakeLockMask = + WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM | WAKE_LOCK_STAY_AWAKE; + return (mWakeLockSummary & (screenOnWakeLockMask)) != 0; + } + public void setWakeLockSummaryLocked(int summary) { mWakeLockSummary = summary; } @@ -419,16 +437,14 @@ public class PowerGroup { return mDisplayPowerRequest.policy; } - boolean updateLocked(float screenBrightnessOverride, boolean autoBrightness, - boolean useProximitySensor, boolean boostScreenBrightness, int dozeScreenState, - float dozeScreenBrightness, boolean overrideDrawWakeLock, - PowerSaveState powerSaverState, boolean quiescent, boolean dozeAfterScreenOff, - boolean vrModeEnabled, boolean bootCompleted, boolean screenBrightnessBoostInProgress, - boolean waitForNegativeProximity) { + boolean updateLocked(float screenBrightnessOverride, boolean useProximitySensor, + boolean boostScreenBrightness, int dozeScreenState, float dozeScreenBrightness, + boolean overrideDrawWakeLock, PowerSaveState powerSaverState, boolean quiescent, + boolean dozeAfterScreenOff, boolean vrModeEnabled, boolean bootCompleted, + boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity) { mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(quiescent, dozeAfterScreenOff, vrModeEnabled, bootCompleted, screenBrightnessBoostInProgress); mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; - mDisplayPowerRequest.useAutoBrightness = autoBrightness; mDisplayPowerRequest.useProximitySensor = useProximitySensor; mDisplayPowerRequest.boostScreenBrightness = boostScreenBrightness; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b1c986e6558a..651bb930c49b 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -584,10 +584,6 @@ public final class PowerManagerService extends SystemService private boolean mIsFaceDown = false; private long mLastFlipTime = 0L; - // The screen brightness mode. - // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants. - private int mScreenBrightnessModeSetting; - // The screen brightness setting override from the window manager // to allow the current foreground activity to override the brightness. private float mScreenBrightnessOverrideFromWindowManager = @@ -1481,10 +1477,6 @@ public final class PowerManagerService extends SystemService mSystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue); } - mScreenBrightnessModeSetting = Settings.System.getIntForUser(resolver, - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); - mDirty |= DIRTY_SETTINGS; } @@ -3466,23 +3458,18 @@ public final class PowerManagerService extends SystemService final PowerGroup powerGroup = mPowerGroups.valueAt(idx); final int groupId = powerGroup.getGroupId(); - // Determine appropriate screen brightness and auto-brightness adjustments. - final boolean autoBrightness; + // Determine appropriate screen brightness. final float screenBrightnessOverride; if (!mBootCompleted) { // Keep the brightness steady during boot. This requires the // bootloader brightness and the default brightness to be identical. - autoBrightness = false; screenBrightnessOverride = mScreenBrightnessDefault; } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { - autoBrightness = false; screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; } else { - autoBrightness = (mScreenBrightnessModeSetting - == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; } - boolean ready = powerGroup.updateLocked(screenBrightnessOverride, autoBrightness, + boolean ready = powerGroup.updateLocked(screenBrightnessOverride, shouldUseProximitySensorLocked(), shouldBoostScreenBrightness(), mDozeScreenStateOverrideFromDreamManager, mDozeScreenBrightnessOverrideFromDreamManagerFloat, @@ -3503,7 +3490,6 @@ public final class PowerManagerService extends SystemService powerGroup.getUserActivitySummaryLocked()) + ", mBootCompleted=" + mBootCompleted + ", screenBrightnessOverride=" + screenBrightnessOverride - + ", useAutoBrightness=" + autoBrightness + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress + ", mIsVrModeEnabled= " + mIsVrModeEnabled @@ -4525,7 +4511,6 @@ public final class PowerManagerService extends SystemService + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced=" + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")"); pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting); - pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting); pw.println(" mScreenBrightnessOverrideFromWindowManager=" + mScreenBrightnessOverrideFromWindowManager); pw.println(" mUserActivityTimeoutOverrideFromWindowManager=" @@ -4904,9 +4889,6 @@ public final class PowerManagerService extends SystemService proto.end(stayOnWhilePluggedInToken); proto.write( - PowerServiceSettingsAndConfigurationDumpProto.SCREEN_BRIGHTNESS_MODE_SETTING, - mScreenBrightnessModeSetting); - proto.write( PowerServiceSettingsAndConfigurationDumpProto .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER, mScreenBrightnessOverrideFromWindowManager); @@ -5778,6 +5760,11 @@ public final class PowerManagerService extends SystemService try { synchronized (mLock) { PowerGroup defaultPowerGroup = mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP); + if ((flags & PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP) != 0) { + if (defaultPowerGroup.hasWakeLockKeepingScreenOnLocked()) { + return; + } + } if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { sleepPowerGroupLocked(defaultPowerGroup, eventTime, reason, uid); } else { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 2888b9a2d3cc..afe8d3eb2c54 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -563,7 +563,12 @@ public class TrustManagerService extends SystemService { changed = mUserIsTrusted.get(userId) != trusted; mUserIsTrusted.put(userId, trusted); } - dispatchOnTrustChanged(trusted, userId, flags, getTrustGrantedMessages(userId)); + dispatchOnTrustChanged( + trusted, + false /* newlyUnlocked */, + userId, + flags, + getTrustGrantedMessages(userId)); if (changed) { refreshDeviceLockedForUser(userId); if (!trusted) { @@ -628,7 +633,9 @@ public class TrustManagerService extends SystemService { if (DEBUG) Slog.d(TAG, "pendingTrustState: " + pendingTrustState); boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED; - dispatchOnTrustChanged(isNowTrusted, userId, flags, getTrustGrantedMessages(userId)); + boolean newlyUnlocked = !alreadyUnlocked && isNowTrusted; + dispatchOnTrustChanged( + isNowTrusted, newlyUnlocked, userId, flags, getTrustGrantedMessages(userId)); if (isNowTrusted != wasTrusted) { refreshDeviceLockedForUser(userId); if (!isNowTrusted) { @@ -643,8 +650,7 @@ public class TrustManagerService extends SystemService { } } - boolean wasLocked = !alreadyUnlocked; - boolean shouldSendCallback = wasLocked && pendingTrustState == TrustState.TRUSTED; + boolean shouldSendCallback = newlyUnlocked; if (shouldSendCallback) { if (resultCallback != null) { if (DEBUG) Slog.d(TAG, "calling back with UNLOCKED_BY_GRANT"); @@ -690,7 +696,7 @@ public class TrustManagerService extends SystemService { */ public void lockUser(int userId) { mLockPatternUtils.requireStrongAuth( - StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); + StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, userId); try { WindowManagerGlobal.getWindowManagerService().lockNow(null); } catch (RemoteException e) { @@ -1387,16 +1393,17 @@ public class TrustManagerService extends SystemService { } } - private void dispatchOnTrustChanged(boolean enabled, int userId, int flags, - @NonNull List<String> trustGrantedMessages) { + private void dispatchOnTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, + int flags, @NonNull List<String> trustGrantedMessages) { if (DEBUG) { - Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x" + Log.i(TAG, "onTrustChanged(" + enabled + ", " + newlyUnlocked + ", " + userId + ", 0x" + Integer.toHexString(flags) + ")"); } if (!enabled) flags = 0; for (int i = 0; i < mTrustListeners.size(); i++) { try { - mTrustListeners.get(i).onTrustChanged(enabled, userId, flags, trustGrantedMessages); + mTrustListeners.get(i).onTrustChanged( + enabled, newlyUnlocked, userId, flags, trustGrantedMessages); } catch (DeadObjectException e) { Slog.d(TAG, "Removing dead TrustListener."); mTrustListeners.remove(i); @@ -2087,7 +2094,7 @@ public class TrustManagerService extends SystemService { if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) { if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout"); mLockPatternUtils.requireStrongAuth( - mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId); + mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId); } maybeLockScreen(mUserId); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e639866a6bab..418e1edca05b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7756,10 +7756,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // configuration. This is important to cases where activities with incompatible // orientations launch, or user goes back from an activity of bi-orientation to an // activity with specified orientation. - if (getRequestedOrientation() == SCREEN_ORIENTATION_UNSET) { - return; - } - if (onDescendantOrientationChanged(this)) { // WM Shell can show additional UI elements, e.g. a restart button for size compat mode // so ensure that WM Shell is called when an activity becomes visible. @@ -8329,7 +8325,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If orientation is respected when insets are applied, then stableBounds will be empty. boolean orientationRespectedWithInsets = orientationRespectedWithInsets(parentBounds, stableBounds); - if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { + if (orientationRespectedWithInsets + && handlesOrientationChangeFromDescendant(mOrientation)) { // No need to letterbox because of fixed orientation. Display will handle // fixed-orientation requests and a display rotation is enough to respect requested // orientation with insets applied. diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index a32e46078d82..af5bd14baf31 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -34,6 +34,8 @@ import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; import android.annotation.Nullable; +import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; @@ -141,26 +143,30 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override + @ScreenOrientation int getOrientation(int candidate) { - mLastOrientationSource = null; - if (getIgnoreOrientationRequest()) { + final int orientation = super.getOrientation(candidate); + if (getIgnoreOrientationRequest(orientation)) { + // In all the other case, mLastOrientationSource will be reassigned to a new value + mLastOrientationSource = null; return SCREEN_ORIENTATION_UNSET; } - - return super.getOrientation(candidate); + return orientation; } @Override - boolean handlesOrientationChangeFromDescendant() { - return !getIgnoreOrientationRequest() - && super.handlesOrientationChangeFromDescendant(); + boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { + return !getIgnoreOrientationRequest(orientation) + && super.handlesOrientationChangeFromDescendant(orientation); } @Override - boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { // If this is set to ignore the orientation request, we don't propagate descendant // orientation request. - return !getIgnoreOrientationRequest() + final int orientation = requestingContainer != null + ? requestingContainer.mOrientation : SCREEN_ORIENTATION_UNSET; + return !getIgnoreOrientationRequest(orientation) && super.onDescendantOrientationChanged(requestingContainer); } @@ -224,6 +230,23 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } } + /** + * @return {@value true} if we need to ignore the orientation in input. + */ + // TODO(b/262366204): Rename getIgnoreOrientationRequest to shouldIgnoreOrientationRequest + boolean getIgnoreOrientationRequest(@ScreenOrientation int orientation) { + // We always respect orientation request for ActivityInfo.SCREEN_ORIENTATION_LOCKED + // ActivityInfo.SCREEN_ORIENTATION_NOSENSOR. + // Main use case why this is important is Camera apps that rely on those + // properties to ensure that they will be able to determine Camera preview + // orientation correctly + if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED + || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + return false; + } + return getIgnoreOrientationRequest(); + } + boolean getIgnoreOrientationRequest() { // Adding an exception for when ignoreOrientationRequest is overridden at runtime for all // DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't @@ -640,11 +663,9 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override + @ScreenOrientation int getOrientation(int candidate) { mLastOrientationSource = null; - if (getIgnoreOrientationRequest()) { - return SCREEN_ORIENTATION_UNSET; - } // Find a window requesting orientation. final WindowState win = getWindow(mGetOrientingWindow); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5d502f5420ca..4c499861cb9a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1558,13 +1558,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { final Configuration config = updateOrientation( requestingContainer, false /* forceUpdate */); // If display rotation class tells us that it doesn't consider app requested orientation, // this display won't rotate just because of an app changes its requested orientation. Thus // it indicates that this display chooses not to handle this request. - final boolean handled = handlesOrientationChangeFromDescendant(); + final int orientation = requestingContainer != null ? requestingContainer.mOrientation + : SCREEN_ORIENTATION_UNSET; + final boolean handled = handlesOrientationChangeFromDescendant(orientation); if (config == null) { return handled; } @@ -1587,8 +1589,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - boolean handlesOrientationChangeFromDescendant() { - return !getIgnoreOrientationRequest() + boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { + return !getIgnoreOrientationRequest(orientation) && !getDisplayRotation().isFixedToUserRotation(); } @@ -1689,7 +1691,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return ROTATION_UNDEFINED; } if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM - || getIgnoreOrientationRequest()) { + || getIgnoreOrientationRequest(r.mOrientation)) { return ROTATION_UNDEFINED; } if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { @@ -2688,15 +2690,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @ScreenOrientation @Override int getOrientation() { - mLastOrientationSource = null; - if (!handlesOrientationChangeFromDescendant()) { - // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation - ProtoLog.v(WM_DEBUG_ORIENTATION, - "Display id=%d is ignoring all orientation requests, return %d", - mDisplayId, SCREEN_ORIENTATION_UNSPECIFIED); - return SCREEN_ORIENTATION_UNSPECIFIED; - } - if (mWmService.mDisplayFrozen) { if (mWmService.mPolicy.isKeyguardLocked()) { // Use the last orientation the while the display is frozen with the keyguard @@ -2712,6 +2705,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final int orientation = super.getOrientation(); + + if (!handlesOrientationChangeFromDescendant(orientation)) { + mLastOrientationSource = null; + // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Display id=%d is ignoring orientation request for %d, return %d", + mDisplayId, orientation, SCREEN_ORIENTATION_UNSPECIFIED); + return SCREEN_ORIENTATION_UNSPECIFIED; + } + if (orientation == SCREEN_ORIENTATION_UNSET) { // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation ProtoLog.v(WM_DEBUG_ORIENTATION, @@ -3832,18 +3835,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */ void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) { - // Only record the TaskDisplayArea that handles orientation request. - if (taskDisplayArea != null && taskDisplayArea.handlesOrientationChangeFromDescendant()) { - mOrientationRequestingTaskDisplayArea = taskDisplayArea; - return; - } - - // If the previous TDA no longer handles orientation request, clear it. - if (mOrientationRequestingTaskDisplayArea != null - && !mOrientationRequestingTaskDisplayArea - .handlesOrientationChangeFromDescendant()) { - mOrientationRequestingTaskDisplayArea = null; - } + mOrientationRequestingTaskDisplayArea = taskDisplayArea; } /** @@ -5053,13 +5045,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - int getOrientation(int candidate) { - if (getIgnoreOrientationRequest()) { - return SCREEN_ORIENTATION_UNSET; - } - + @ScreenOrientation + int getOrientation(@ScreenOrientation int candidate) { // IME does not participate in orientation. - return candidate; + return getIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate; } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 292ed951b7e7..ea6f2442a919 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -143,6 +143,7 @@ import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -2686,8 +2687,8 @@ class Task extends TaskFragment { } @Override - boolean handlesOrientationChangeFromDescendant() { - if (!super.handlesOrientationChangeFromDescendant()) { + boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { + if (!super.handlesOrientationChangeFromDescendant(orientation)) { return false; } @@ -2702,7 +2703,7 @@ class Task extends TaskFragment { // Check for leaf Task. // Display won't rotate for the orientation request if the Task/TaskDisplayArea // can't specify orientation. - return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation(); + return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation(orientation); } void resize(boolean relayout, boolean forced) { @@ -2772,6 +2773,13 @@ class Task extends TaskFragment { @Override void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, Rect outSurfaceInsets) { + // If this task has its adjacent task, it means they should animate together. Use display + // bounds for them could move same as full screen task. + if (getAdjacentTaskFragment() != null && getAdjacentTaskFragment().asTask() != null) { + super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets); + return; + } + final WindowState windowState = getTopVisibleAppMainWindow(); if (windowState != null) { windowState.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets); @@ -4920,6 +4928,11 @@ class Task extends TaskFragment { } if (child.getVisibility(null /* starting */) != TASK_FRAGMENT_VISIBILITY_VISIBLE) { + if (child.topRunningActivity() == null) { + // Skip the task if no running activity and continue resuming next task. + continue; + } + // Otherwise, assuming everything behind this task should also be invisible. break; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index e0ed356fbe59..8ad76a3eb327 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -41,6 +41,7 @@ import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.graphics.Color; import android.os.UserHandle; @@ -633,22 +634,20 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } @Override - int getOrientation(int candidate) { - mLastOrientationSource = null; - if (getIgnoreOrientationRequest()) { - return SCREEN_ORIENTATION_UNSET; - } - if (!canSpecifyOrientation()) { + @ScreenOrientation + int getOrientation(@ScreenOrientation int candidate) { + final int orientation = super.getOrientation(candidate); + if (!canSpecifyOrientation(orientation)) { + mLastOrientationSource = null; // We only respect orientation of the focused TDA, which can be a child of this TDA. - return reduceOnAllTaskDisplayAreas((taskDisplayArea, orientation) -> { - if (taskDisplayArea == this || orientation != SCREEN_ORIENTATION_UNSET) { - return orientation; + return reduceOnAllTaskDisplayAreas((taskDisplayArea, taskOrientation) -> { + if (taskDisplayArea == this || taskOrientation != SCREEN_ORIENTATION_UNSET) { + return taskOrientation; } return taskDisplayArea.getOrientation(candidate); }, SCREEN_ORIENTATION_UNSET); } - final int orientation = super.getOrientation(candidate); if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { ProtoLog.v(WM_DEBUG_ORIENTATION, @@ -1870,12 +1869,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } /** Whether this task display area can request orientation. */ - boolean canSpecifyOrientation() { - // Only allow to specify orientation if this TDA is not set to ignore orientation request, - // and it is the last focused one on this logical display that can request orientation - // request. - return !getIgnoreOrientationRequest() - && mDisplayContent.getOrientationRequestingTaskDisplayArea() == this; + boolean canSpecifyOrientation(@ScreenOrientation int orientation) { + // Only allow to specify orientation if this TDA is the last focused one on this logical + // display that can request orientation request. + return mDisplayContent.getOrientationRequestingTaskDisplayArea() == this + && !getIgnoreOrientationRequest(orientation); } void clearPreferredTopFocusableRootTask() { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index ae4f8947cc86..f6db3f7cbb36 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -227,11 +227,16 @@ class TaskFragment extends WindowContainer<WindowContainer> { private TaskFragment mCompanionTaskFragment; /** - * Prevents duplicate calls to onTaskAppeared. + * Prevents duplicate calls to onTaskFragmentAppeared. */ boolean mTaskFragmentAppearedSent; /** + * Prevents unnecessary callbacks after onTaskFragmentVanished. + */ + boolean mTaskFragmentVanishedSent; + + /** * The last running activity of the TaskFragment was finished due to clear task while launching * an activity in the Task. */ @@ -2223,7 +2228,8 @@ 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()) { + } else if (windowingMode == WINDOWING_MODE_MULTI_WINDOW + && isEmbeddedWithBoundsOverride()) { // For embedded TFs, the smallest width should be updated. Otherwise, inherit // from the parent task would result in applications loaded wrong resource. inOutConfig.smallestScreenWidthDp = @@ -2552,7 +2558,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { mRemoteToken.toWindowContainerToken(), getConfiguration(), getNonFinishingActivityCount(), - isVisibleRequested(), + shouldBeVisible(null /* starting */), childActivities, positionInParent, mClearedTaskForReuse, @@ -2848,6 +2854,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (parentTf != null) { parentTf.onActivityVisibleRequestedChanged(); } + // Send the info changed to update the TaskFragment visibility. + sendTaskFragmentInfoChanged(); } @Nullable diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 6e4df794f8d8..90a0dffa25f2 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -553,6 +553,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer, @NonNull TaskFragment taskFragment) { + if (taskFragment.mTaskFragmentVanishedSent) { + return; + } if (taskFragment.getTask() == null) { Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf=" + taskFragment); @@ -574,6 +577,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer, @NonNull TaskFragment taskFragment) { + if (taskFragment.mTaskFragmentVanishedSent) { + return; + } validateAndGetState(organizer); if (!taskFragment.mTaskFragmentAppearedSent) { // Skip if TaskFragment still not appeared. @@ -586,10 +592,6 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr .setTaskFragment(taskFragment) .build(); } else { - if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) { - // Skipped the info changed event if vanished event is pending. - return; - } // Remove and add for re-ordering. removePendingEvent(pendingEvent); // Reset the defer time when TaskFragment is changed, so that it can check again if @@ -602,6 +604,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer, @NonNull TaskFragment taskFragment) { + if (taskFragment.mTaskFragmentVanishedSent) { + return; + } + taskFragment.mTaskFragmentVanishedSent = true; final TaskFragmentOrganizerState state = validateAndGetState(organizer); final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents .get(organizer.asBinder()); @@ -617,20 +623,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr .setTaskFragment(taskFragment) .build()); state.removeTaskFragment(taskFragment); + // Make sure the vanished event will be dispatched if there are no other changes. + mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal(); } void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment, int opType, @NonNull Throwable exception) { - validateAndGetState(organizer); - Slog.w(TAG, "onTaskFragmentError ", exception); - final PendingTaskFragmentEvent vanishedEvent = taskFragment != null - ? getPendingTaskFragmentEvent(taskFragment, PendingTaskFragmentEvent.EVENT_VANISHED) - : null; - if (vanishedEvent != null) { - // No need to notify if the TaskFragment has been removed. + if (taskFragment != null && taskFragment.mTaskFragmentVanishedSent) { return; } + validateAndGetState(organizer); + Slog.w(TAG, "onTaskFragmentError ", exception); addPendingEvent(new PendingTaskFragmentEvent.Builder( PendingTaskFragmentEvent.EVENT_ERROR, organizer) .setErrorCallbackToken(errorCallbackToken) diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5c893de6b920..cb5a4338c567 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -1419,9 +1420,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @return {@code true} if it handles or will handle orientation change in the future; {@code * false} if it won't handle the change at anytime. */ - boolean handlesOrientationChangeFromDescendant() { + boolean handlesOrientationChangeFromDescendant(int orientation) { final WindowContainer parent = getParent(); - return parent != null && parent.handlesOrientationChangeFromDescendant(); + return parent != null && parent.handlesOrientationChangeFromDescendant(orientation); } /** @@ -1513,7 +1514,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // portrait but the task is still in landscape. While updating from display, // the task can be updated to portrait first so the configuration can be // computed in a consistent environment. - && (inMultiWindowMode() || !handlesOrientationChangeFromDescendant())) { + && (inMultiWindowMode() + || !handlesOrientationChangeFromDescendant(orientation))) { // Resolve the requested orientation. onConfigurationChanged(parent.getConfiguration()); } @@ -3186,7 +3188,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (isOrganized() // TODO(b/161711458): Clean-up when moved to shell. && getWindowingMode() != WINDOWING_MODE_FULLSCREEN - && getWindowingMode() != WINDOWING_MODE_FREEFORM) { + && getWindowingMode() != WINDOWING_MODE_FREEFORM + && getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW) { return null; } diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index f53a1cfcfb3c..7bc89313c75e 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -44,9 +44,11 @@ </xs:element> <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" maxOccurs="1"/> - <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" /> + <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1"/> <xs:element type="autoBrightness" name="autoBrightness" minOccurs="0" - maxOccurs="1" /> + maxOccurs="1"/> + <xs:element type="refreshRateConfigs" name="refreshRate" minOccurs="0" + maxOccurs="1"/> <xs:element type="nonNegativeDecimal" name="screenBrightnessRampFastDecrease"> <xs:annotation name="final"/> </xs:element> @@ -324,7 +326,7 @@ <xs:annotation name="final"/> </xs:element> </xs:sequence> - </xs:complexType> + </xs:complexType> <!-- Thresholds for brightness changes. --> <xs:complexType name="thresholds"> @@ -452,4 +454,35 @@ </xs:element> </xs:sequence> </xs:complexType> + + <xs:complexType name="refreshRateConfigs"> + <xs:element name="lowerBlockingZoneConfigs" type="blockingZoneConfig" + minOccurs="0" maxOccurs="1"> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="higherBlockingZoneConfigs" type="blockingZoneConfig" + minOccurs="0" maxOccurs="1"> + <xs:annotation name="final"/> + </xs:element> + </xs:complexType> + + <xs:complexType name="blockingZoneConfig"> + <xs:element name="defaultRefreshRate" type="xs:nonNegativeInteger" + minOccurs="1" maxOccurs="1"> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="blockingZoneThreshold" type="blockingZoneThreshold" + minOccurs="1" maxOccurs="1"> + <xs:annotation name="final"/> + </xs:element> + </xs:complexType> + + <xs:complexType name="blockingZoneThreshold"> + <xs:sequence> + <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint" + minOccurs="1" maxOccurs="unbounded"> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> </xs:schema> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index d89bd7cc9aa2..6276edaf3ebc 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -13,6 +13,19 @@ package com.android.server.display.config { method public void setEnabled(boolean); } + public class BlockingZoneConfig { + ctor public BlockingZoneConfig(); + method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold(); + method public final java.math.BigInteger getDefaultRefreshRate(); + method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold); + method public final void setDefaultRefreshRate(java.math.BigInteger); + } + + public class BlockingZoneThreshold { + ctor public BlockingZoneThreshold(); + method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint(); + } + public class BrightnessThresholds { ctor public BrightnessThresholds(); method public final com.android.server.display.config.ThresholdPoints getBrightnessThresholdPoints(); @@ -76,6 +89,7 @@ package com.android.server.display.config { method public final com.android.server.display.config.SensorDetails getLightSensor(); method public final com.android.server.display.config.SensorDetails getProxSensor(); method public com.android.server.display.config.DisplayQuirks getQuirks(); + method public com.android.server.display.config.RefreshRateConfigs getRefreshRate(); method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault(); method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap(); method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxMillis(); @@ -97,6 +111,7 @@ package com.android.server.display.config { method public final void setLightSensor(com.android.server.display.config.SensorDetails); method public final void setProxSensor(com.android.server.display.config.SensorDetails); method public void setQuirks(com.android.server.display.config.DisplayQuirks); + method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs); method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal); method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap); method public final void setScreenBrightnessRampDecreaseMaxMillis(java.math.BigInteger); @@ -160,6 +175,14 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public class RefreshRateConfigs { + ctor public RefreshRateConfigs(); + method public final com.android.server.display.config.BlockingZoneConfig getHigherBlockingZoneConfigs(); + method public final com.android.server.display.config.BlockingZoneConfig getLowerBlockingZoneConfigs(); + method public final void setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig); + method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig); + } + public class RefreshRateRange { ctor public RefreshRateRange(); method public final java.math.BigInteger getMaximum(); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index ed369c016770..82236bfd98e0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -155,6 +155,18 @@ public class LocalDisplayAdapterTest { when(mMockedResources.getIntArray( com.android.internal.R.array.config_autoBrightnessLevels)) .thenReturn(new int[]{}); + when(mMockedResources.getIntArray( + com.android.internal.R.array.config_brightnessThresholdsOfPeakRefreshRate)) + .thenReturn(new int[]{}); + when(mMockedResources.getIntArray( + com.android.internal.R.array.config_ambientThresholdsOfPeakRefreshRate)) + .thenReturn(new int[]{}); + when(mMockedResources.getIntArray( + com.android.internal.R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate)) + .thenReturn(new int[]{}); + when(mMockedResources.getIntArray( + com.android.internal.R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate)) + .thenReturn(new int[]{}); } @After diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java index 7f341fff514d..6b705aaf9721 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -150,6 +150,16 @@ public final class DisplayDeviceConfigTest { assertEquals("ProximitySensor123", mDisplayDeviceConfig.getProximitySensor().name); assertEquals("prox_type_1", mDisplayDeviceConfig.getProximitySensor().type); + assertEquals(75, mDisplayDeviceConfig.getDefaultLowRefreshRate()); + assertEquals(90, mDisplayDeviceConfig.getDefaultHighRefreshRate()); + assertArrayEquals(new int[]{45, 55}, + mDisplayDeviceConfig.getLowDisplayBrightnessThresholds()); + assertArrayEquals(new int[]{50, 60}, + mDisplayDeviceConfig.getLowAmbientBrightnessThresholds()); + assertArrayEquals(new int[]{65, 75}, + mDisplayDeviceConfig.getHighDisplayBrightnessThresholds()); + assertArrayEquals(new int[]{70, 80}, + mDisplayDeviceConfig.getHighAmbientBrightnessThresholds()); // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping, // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor. @@ -212,9 +222,8 @@ public final class DisplayDeviceConfigTest { mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle(), ZERO_DELTA); assertArrayEquals(new float[]{29, 30, 31}, mDisplayDeviceConfig.getAmbientDarkeningPercentagesIdle(), ZERO_DELTA); - - assertEquals(mDisplayDeviceConfig.getDefaultRefreshRate(), DEFAULT_REFRESH_RATE); - assertEquals(mDisplayDeviceConfig.getDefaultPeakRefreshRate(), DEFAULT_PEAK_REFRESH_RATE); + assertEquals(mDisplayDeviceConfig.getDefaultLowRefreshRate(), DEFAULT_REFRESH_RATE); + assertEquals(mDisplayDeviceConfig.getDefaultHighRefreshRate(), DEFAULT_PEAK_REFRESH_RATE); assertArrayEquals(mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(), LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE); assertArrayEquals(mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(), @@ -426,6 +435,38 @@ public final class DisplayDeviceConfigTest { + "<name>ProximitySensor123</name>\n" + "<type>prox_type_1</type>\n" + "</proxSensor>\n" + + "<refreshRate>\n" + + "<lowerBlockingZoneConfigs>\n" + + "<defaultRefreshRate>75</defaultRefreshRate>\n" + + "<blockingZoneThreshold>\n" + + "<displayBrightnessPoint>\n" + + "<lux>50</lux>\n" + // This number will be rounded to integer when read by the system + + "<nits>45.3</nits>\n" + + "</displayBrightnessPoint>\n" + + "<displayBrightnessPoint>\n" + + "<lux>60</lux>\n" + // This number will be rounded to integer when read by the system + + "<nits>55.2</nits>\n" + + "</displayBrightnessPoint>\n" + + "</blockingZoneThreshold>\n" + + "</lowerBlockingZoneConfigs>\n" + + "<higherBlockingZoneConfigs>\n" + + "<defaultRefreshRate>90</defaultRefreshRate>\n" + + "<blockingZoneThreshold>\n" + + "<displayBrightnessPoint>\n" + + "<lux>70</lux>\n" + // This number will be rounded to integer when read by the system + + "<nits>65.6</nits>\n" + + "</displayBrightnessPoint>\n" + + "<displayBrightnessPoint>\n" + + "<lux>80</lux>\n" + // This number will be rounded to integer when read by the system + + "<nits>75</nits>\n" + + "</displayBrightnessPoint>\n" + + "</blockingZoneThreshold>\n" + + "</higherBlockingZoneConfigs>\n" + + "</refreshRate>\n" + "</displayConfiguration>\n"; } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index fb0cdfa9a680..cfea63babac3 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -1896,8 +1896,8 @@ public class DisplayModeDirectorTest { // Notify that the default display is updated, such that DisplayDeviceConfig has new values DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class); - when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(50); - when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(55); + when(displayDeviceConfig.getDefaultLowRefreshRate()).thenReturn(50); + when(displayDeviceConfig.getDefaultHighRefreshRate()).thenReturn(55); when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25}); when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30}); when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210}); diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java index d515fae4afe2..638637d544de 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -18,10 +18,14 @@ package com.android.server.display; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY_GROUP; +import static android.view.Display.TYPE_INTERNAL; +import static android.view.Display.TYPE_VIRTUAL; +import static com.android.server.display.DeviceStateToLayoutMap.STATE_DEFAULT; import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED; import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED; import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED; +import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY; import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED; import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED; @@ -69,7 +73,6 @@ import org.mockito.Spy; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; -import java.util.Set; @SmallTest @Presubmit @@ -151,8 +154,8 @@ public class LogicalDisplayMapperTest { @Test public void testDisplayDeviceAddAndRemove_Internal() { - DisplayDevice device = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); // add LogicalDisplay displayAdded = add(device); @@ -173,7 +176,7 @@ public class LogicalDisplayMapperTest { testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_EXTERNAL); testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_WIFI); testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_OVERLAY); - testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_VIRTUAL); + testDisplayDeviceAddAndRemove_NonInternal(TYPE_VIRTUAL); testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_UNKNOWN); // Call the internal test again, just to verify that adding non-internal displays @@ -183,9 +186,9 @@ public class LogicalDisplayMapperTest { @Test public void testDisplayDeviceAdd_TwoInternalOneDefault() { - DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0); - DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, 0); + DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); LogicalDisplay display1 = add(device1); assertEquals(info(display1).address, info(device1).address); @@ -198,10 +201,10 @@ public class LogicalDisplayMapperTest { @Test public void testDisplayDeviceAdd_TwoInternalBothDefault() { - DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); - DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); LogicalDisplay display1 = add(device1); assertEquals(info(display1).address, info(device1).address); @@ -216,7 +219,7 @@ public class LogicalDisplayMapperTest { @Test public void testDisplayDeviceAddAndRemove_OneExternalDefault() { DisplayDevice device = createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); // add LogicalDisplay displayAdded = add(device); @@ -234,10 +237,10 @@ public class LogicalDisplayMapperTest { @Test public void testDisplayDeviceAddAndRemove_SwitchDefault() { - DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); - DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); LogicalDisplay display1 = add(device1); assertEquals(info(display1).address, info(device1).address); @@ -263,10 +266,10 @@ public class LogicalDisplayMapperTest { @Test public void testGetDisplayIdsLocked() { - add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); + add(createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0)); - add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0)); + add(createDisplayDevice(TYPE_VIRTUAL, 600, 800, 0)); int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID, /* includeDisabled= */ true); @@ -276,71 +279,98 @@ public class LogicalDisplayMapperTest { } @Test - public void testGetDisplayInfoForStateLocked_oneDisplayGroup_internalType() { - add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - add(createDisplayDevice(Display.TYPE_INTERNAL, 700, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - - Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked( - DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP); - assertThat(displayInfos.size()).isEqualTo(1); - for (DisplayInfo displayInfo : displayInfos) { - assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY); - assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP); - assertThat(displayInfo.logicalWidth).isEqualTo(600); - assertThat(displayInfo.logicalHeight).isEqualTo(800); - } - } + public void testGetDisplayInfoForStateLocked_defaultLayout() { + final DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + final DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 200, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); - @Test - public void testGetDisplayInfoForStateLocked_oneDisplayGroup_differentTypes() { - add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - add(createDisplayDevice(Display.TYPE_EXTERNAL, 700, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - - Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked( - DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP); - assertThat(displayInfos.size()).isEqualTo(1); - for (DisplayInfo displayInfo : displayInfos) { - assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY); - assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP); - assertThat(displayInfo.logicalWidth).isEqualTo(600); - assertThat(displayInfo.logicalHeight).isEqualTo(800); - } + add(device1); + add(device2); + + Layout layout1 = new Layout(); + layout1.createDisplayLocked(info(device1).address, /* isDefault= */ true, + /* isEnabled= */ true); + layout1.createDisplayLocked(info(device2).address, /* isDefault= */ false, + /* isEnabled= */ true); + when(mDeviceStateToLayoutMapSpy.get(STATE_DEFAULT)).thenReturn(layout1); + assertThat(layout1.size()).isEqualTo(2); + final int logicalId2 = layout1.getByAddress(info(device2).address).getLogicalDisplayId(); + + final DisplayInfo displayInfoDefault = mLogicalDisplayMapper.getDisplayInfoForStateLocked( + STATE_DEFAULT, DEFAULT_DISPLAY); + assertThat(displayInfoDefault.displayId).isEqualTo(DEFAULT_DISPLAY); + assertThat(displayInfoDefault.logicalWidth).isEqualTo(width(device1)); + assertThat(displayInfoDefault.logicalHeight).isEqualTo(height(device1)); + + final DisplayInfo displayInfoOther = mLogicalDisplayMapper.getDisplayInfoForStateLocked( + STATE_DEFAULT, logicalId2); + assertThat(displayInfoOther).isNotNull(); + assertThat(displayInfoOther.displayId).isEqualTo(logicalId2); + assertThat(displayInfoOther.logicalWidth).isEqualTo(width(device2)); + assertThat(displayInfoOther.logicalHeight).isEqualTo(height(device2)); } @Test - public void testGetDisplayInfoForStateLocked_multipleDisplayGroups_defaultGroup() { - add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - add(createDisplayDevice(Display.TYPE_VIRTUAL, 700, 800, - DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP)); - - Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked( - DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP); - assertThat(displayInfos.size()).isEqualTo(1); - for (DisplayInfo displayInfo : displayInfos) { - assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY); - assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP); - assertThat(displayInfo.logicalWidth).isEqualTo(600); - assertThat(displayInfo.logicalHeight).isEqualTo(800); - } + public void testGetDisplayInfoForStateLocked_multipleLayouts() { + final DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + final DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 200, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + final DisplayDevice device3 = createDisplayDevice(TYPE_VIRTUAL, 700, 800, + DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP); + + add(device1); + add(device2); + add(device3); + + Layout layout1 = new Layout(); + layout1.createDisplayLocked(info(device1).address, + /* isDefault= */ true, /* isEnabled= */ true); + when(mDeviceStateToLayoutMapSpy.get(STATE_DEFAULT)).thenReturn(layout1); + + final int layoutState2 = 2; + Layout layout2 = new Layout(); + layout2.createDisplayLocked(info(device2).address, + /* isDefault= */ false, /* isEnabled= */ true); + // Device3 is the default display. + layout2.createDisplayLocked(info(device3).address, + /* isDefault= */ true, /* isEnabled= */ true); + when(mDeviceStateToLayoutMapSpy.get(layoutState2)).thenReturn(layout2); + assertThat(layout2.size()).isEqualTo(2); + final int logicalId2 = layout2.getByAddress(info(device2).address).getLogicalDisplayId(); + + // Default layout. + final DisplayInfo displayInfoLayout1Default = + mLogicalDisplayMapper.getDisplayInfoForStateLocked( + STATE_DEFAULT, DEFAULT_DISPLAY); + assertThat(displayInfoLayout1Default.displayId).isEqualTo(DEFAULT_DISPLAY); + assertThat(displayInfoLayout1Default.logicalWidth).isEqualTo(width(device1)); + assertThat(displayInfoLayout1Default.logicalHeight).isEqualTo(height(device1)); + + // Second layout, where device3 is the default display. + final DisplayInfo displayInfoLayout2Default = + mLogicalDisplayMapper.getDisplayInfoForStateLocked( + layoutState2, DEFAULT_DISPLAY); + assertThat(displayInfoLayout2Default.displayId).isEqualTo(DEFAULT_DISPLAY); + assertThat(displayInfoLayout2Default.logicalWidth).isEqualTo(width(device3)); + assertThat(displayInfoLayout2Default.logicalHeight).isEqualTo(height(device3)); + + final DisplayInfo displayInfoLayout2Other = + mLogicalDisplayMapper.getDisplayInfoForStateLocked( + layoutState2, logicalId2); + assertThat(displayInfoLayout2Other).isNotNull(); + assertThat(displayInfoLayout2Other.displayId).isEqualTo(logicalId2); + assertThat(displayInfoLayout2Other.logicalWidth).isEqualTo(width(device2)); + assertThat(displayInfoLayout2Other.logicalHeight).isEqualTo(height(device2)); } @Test public void testSingleDisplayGroup() { - LogicalDisplay display1 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0)); - LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0)); + LogicalDisplay display1 = add(createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); + LogicalDisplay display2 = add(createDisplayDevice(TYPE_INTERNAL, 600, 800, 0)); + LogicalDisplay display3 = add(createDisplayDevice(TYPE_VIRTUAL, 600, 800, 0)); assertEquals(DEFAULT_DISPLAY_GROUP, mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1))); @@ -352,12 +382,12 @@ public class LogicalDisplayMapperTest { @Test public void testMultipleDisplayGroups() { - LogicalDisplay display1 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); - LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0)); + LogicalDisplay display1 = add(createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY)); + LogicalDisplay display2 = add(createDisplayDevice(TYPE_INTERNAL, 600, 800, 0)); - TestDisplayDevice device3 = createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, + TestDisplayDevice device3 = createDisplayDevice(TYPE_VIRTUAL, 600, 800, DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP); LogicalDisplay display3 = add(device3); @@ -423,10 +453,10 @@ public class LogicalDisplayMapperTest { @Test public void testDeviceStateLocked() { - DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); - DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); Layout layout = new Layout(); layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, true, true); @@ -479,13 +509,13 @@ public class LogicalDisplayMapperTest { DisplayAddress displayAddressTwo = new TestUtils.TestDisplayAddress(); DisplayAddress displayAddressThree = new TestUtils.TestDisplayAddress(); - TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, Display.TYPE_INTERNAL, + TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, TYPE_INTERNAL, 600, 800, - DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); - TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, Display.TYPE_INTERNAL, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, TYPE_INTERNAL, 200, 800, DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP); - TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, Display.TYPE_INTERNAL, + TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, TYPE_INTERNAL, 600, 900, DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP); Layout threeDevicesEnabledLayout = new Layout(); @@ -502,7 +532,7 @@ public class LogicalDisplayMapperTest { /* isDefault= */ false, /* isEnabled= */ true); - when(mDeviceStateToLayoutMapSpy.get(DeviceStateToLayoutMap.STATE_DEFAULT)) + when(mDeviceStateToLayoutMapSpy.get(STATE_DEFAULT)) .thenReturn(threeDevicesEnabledLayout); LogicalDisplay display1 = add(device1); @@ -620,6 +650,14 @@ public class LogicalDisplayMapperTest { return device.getDisplayDeviceInfoLocked(); } + private int width(DisplayDevice device) { + return info(device).width; + } + + private int height(DisplayDevice device) { + return info(device).height; + } + private DisplayInfo info(LogicalDisplay display) { return display.getDisplayInfoLocked(); } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 07009cb9a207..7c7e2ee53f3c 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -75,6 +75,7 @@ import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOO import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID; import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons; +import static com.android.server.net.NetworkPolicyManagerService.normalizeTemplate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -2059,6 +2060,18 @@ public class NetworkPolicyManagerServiceTest { METERED_NO, actualPolicy.template.getMeteredness()); } + @Test + public void testNormalizeTemplate_duplicatedMergedImsiList() { + final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_CARRIER) + .setSubscriberIds(Set.of(TEST_IMSI)).build(); + final String[] mergedImsiGroup = new String[] {TEST_IMSI, TEST_IMSI}; + final ArrayList<String[]> mergedList = new ArrayList<>(); + mergedList.add(mergedImsiGroup); + // Verify the duplicated items in the merged IMSI list won't crash the system. + final NetworkTemplate result = normalizeTemplate(template, mergedList); + assertEquals(template, result); + } + private String formatBlockedStateError(int uid, int rule, boolean metered, boolean backgroundRestricted) { return String.format( diff --git a/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java b/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java index e3ca1707ae0c..5bc750f4f6ac 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java @@ -258,7 +258,6 @@ public class PowerGroupTest { .build(); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ false, /* useProximitySensor= */ false, /* boostScreenBrightness= */ false, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -275,7 +274,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DIM); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(false); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(false); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(false); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -299,7 +297,6 @@ public class PowerGroupTest { mPowerGroup.setWakeLockSummaryLocked(WAKE_LOCK_DOZE); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -316,7 +313,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_ON); @@ -339,7 +335,6 @@ public class PowerGroupTest { assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -356,7 +351,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -378,7 +372,6 @@ public class PowerGroupTest { assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -395,7 +388,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -417,7 +409,6 @@ public class PowerGroupTest { mPowerGroup.sleepLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_TIMEOUT); assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -434,7 +425,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -455,7 +445,6 @@ public class PowerGroupTest { .build(); assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -472,7 +461,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_VR); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -494,7 +482,6 @@ public class PowerGroupTest { mPowerGroup.sleepLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_TIMEOUT); assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -511,7 +498,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -534,7 +520,6 @@ public class PowerGroupTest { assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING); mPowerGroup.setWakeLockSummaryLocked(WAKE_LOCK_SCREEN_BRIGHT); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -551,7 +536,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -572,7 +556,6 @@ public class PowerGroupTest { .build(); assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -589,7 +572,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -611,7 +593,6 @@ public class PowerGroupTest { assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); mPowerGroup.setUserActivitySummaryLocked(USER_ACTIVITY_SCREEN_BRIGHT); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -628,7 +609,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); @@ -649,7 +629,6 @@ public class PowerGroupTest { .build(); assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS, - /* autoBrightness = */ true, /* useProximitySensor= */ true, /* boostScreenBrightness= */ true, /* dozeScreenStateOverride= */ Display.STATE_ON, @@ -666,7 +645,6 @@ public class PowerGroupTest { mPowerGroup.mDisplayPowerRequest; assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT); assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS); - assertThat(displayPowerRequest.useAutoBrightness).isEqualTo(true); assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true); assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true); assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 6325008dc1e0..cbe8e668d7bd 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -492,6 +492,111 @@ public class PowerManagerServiceTest { } @Test + public void testWakefulnessSleep_SoftSleepFlag_NoWakelocks() { + createService(); + // Start with AWAKE state + startSystem(); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + // Take a nap and verify we go to sleep. + mService.getBinderServiceInstance().goToSleep(mClock.now(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, + PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING); + } + + @Test + public void testWakefulnessSleep_SoftSleepFlag_WithPartialWakelock() { + createService(); + // Start with AWAKE state + startSystem(); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + // Grab a wakelock + final String tag = "wakelock1"; + final String packageName = "pkg.name"; + final IBinder token = new Binder(); + final int flags = PowerManager.PARTIAL_WAKE_LOCK; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, + null /* callback */); + + // Take a nap and verify we stay awake. + mService.getBinderServiceInstance().goToSleep(mClock.now(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, + PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING); + } + + @Test + public void testWakefulnessSleep_SoftSleepFlag_WithFullWakelock() { + createService(); + // Start with AWAKE state + startSystem(); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + // Grab a wakelock + final String tag = "wakelock1"; + final String packageName = "pkg.name"; + final IBinder token = new Binder(); + final int flags = PowerManager.FULL_WAKE_LOCK; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, + null /* callback */); + + // Take a nap and verify we stay awake. + mService.getBinderServiceInstance().goToSleep(mClock.now(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, + PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test + public void testWakefulnessSleep_SoftSleepFlag_WithScreenBrightWakelock() { + createService(); + // Start with AWAKE state + startSystem(); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + // Grab a wakelock + final String tag = "wakelock1"; + final String packageName = "pkg.name"; + final IBinder token = new Binder(); + final int flags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, + null /* callback */); + + // Take a nap and verify we stay awake. + mService.getBinderServiceInstance().goToSleep(mClock.now(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, + PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + @Test + public void testWakefulnessSleep_SoftSleepFlag_WithScreenDimWakelock() { + createService(); + // Start with AWAKE state + startSystem(); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + // Grab a wakelock + final String tag = "wakelock1"; + final String packageName = "pkg.name"; + final IBinder token = new Binder(); + final int flags = PowerManager.SCREEN_DIM_WAKE_LOCK; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, + null /* callback */); + + // Take a nap and verify we stay awake. + mService.getBinderServiceInstance().goToSleep(mClock.now(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, + PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnAllowed() { createService(); startSystem(); 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 17ec19d3e617..a017bd6a9436 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -517,7 +517,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Mimic the behavior that display doesn't handle app's requested orientation. final DisplayContent dc = activity.getTask().getDisplayContent(); doReturn(false).when(dc).onDescendantOrientationChanged(any()); - doReturn(false).when(dc).handlesOrientationChangeFromDescendant(); + doReturn(false).when(dc).handlesOrientationChangeFromDescendant(anyInt()); final int requestedOrientation; switch (newConfig.orientation) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index b87c5a364c82..10540dc5a9ee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -471,29 +473,28 @@ public class DisplayAreaTest extends WindowTestsBase { } @Test - public void testSetIgnoreOrientationRequest() { - final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test"); - final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY); - spyOn(token); - doReturn(mock(DisplayContent.class)).when(token).getDisplayContent(); - doNothing().when(token).setParent(any()); - final WindowState win = createWindowState(token); - spyOn(win); - doNothing().when(win).setParent(any()); - win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - token.addChild(win, 0); - area.addChild(token); - doReturn(true).when(win).isVisible(); + public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final ActivityRecord activity = stack.getTopNonFinishingActivity(); - assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation()); + tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation()); + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent, never()).onDescendantOrientationChanged(any()); + + tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + + verify(tda, times(2)).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); } @Test - public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() { + public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedNoSensor() { final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); @@ -501,20 +502,41 @@ public class DisplayAreaTest extends WindowTestsBase { tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); verify(tda).onDescendantOrientationChanged(any()); - verify(mDisplayContent, never()).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); - verify(tda, times(2)).onDescendantOrientationChanged(any()); + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); + } + + @Test + public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedLocked() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final ActivityRecord activity = stack.getTopNonFinishingActivity(); + + tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED); + + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); + + tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED); + + verify(tda).onDescendantOrientationChanged(any()); verify(mDisplayContent).onDescendantOrientationChanged(any()); } @Test - public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() { + public void testGetOrientationRequestingTaskDisplayArea_updateOrientationTaskDisplayArea() { final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); @@ -526,7 +548,7 @@ public class DisplayAreaTest extends WindowTestsBase { // TDA is no longer handling orientation request, clear the last focused TDA. tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull(); + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); // TDA now handles orientation request, update last focused TDA based on the focused app. tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index 3ab4495bd7ca..232b9b2897cc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -17,6 +17,8 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -163,6 +165,30 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } @Test + public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForNoSensor() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + + verify(mFirstRoot).onDescendantOrientationChanged(any()); + verify(mDisplay).onDescendantOrientationChanged(any()); + } + + @Test + public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForLocked() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED); + + verify(mFirstRoot).onDescendantOrientationChanged(any()); + verify(mDisplay).onDescendantOrientationChanged(any()); + } + + @Test public void testLaunchPortraitApp_fillsDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -215,6 +241,21 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } @Test + public void testLaunchNoSensorApp_noSizeCompatAfterRotation() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + + rotateDisplay(mDisplay, ROTATION_90); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + } + + @Test public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -236,6 +277,26 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } @Test + public void testLaunchNoSensorApp_activityIsNotLetterboxForFixedOrientationDisplayAreaGroup() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + } + + @Test + public void testLaunchLockedApp_activityIsNotLetterboxForFixedOrientationInDisplayAreaGroup() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + } + + @Test public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -265,6 +326,20 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } @Test + public void testLaunchNoSensorApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + + rotateDisplay(mDisplay, ROTATION_90); + + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + } + + @Test public void testPlaceImeContainer_reparentToTargetDisplayAreaGroup() { setupImeWindow(); final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index e660db57fb69..582ddf21d7f9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -29,8 +29,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -422,24 +423,63 @@ public class TaskDisplayAreaTests extends WindowTestsBase { // Activity on TDA1 is focused mDisplayContent.setFocusedApp(firstActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + final int testOrientation = SCREEN_ORIENTATION_PORTRAIT; + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); // No focused app, TDA1 is still recorded as last focused. mDisplayContent.setFocusedApp(null); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); // Activity on TDA2 is focused mDisplayContent.setFocusedApp(secondActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + } + + @Test + public void testCanSpecifyOrientation() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstRootTask = firstTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstRootTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondRootTask).build(); + firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + final int testOrientation = SCREEN_ORIENTATION_PORTRAIT; + + // Activity on TDA1 is focused, but TDA1 cannot specify orientation because + // ignoreOrientationRequest is true + // Activity on TDA2 has ignoreOrientationRequest false but it doesn't have focus so it + // cannot specify orientation + mDisplayContent.setFocusedApp(firstActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + + // Activity on TDA1 is not focused, and so it cannot specify orientation + // Activity on TDA2 is focused, and it can specify orientation because + // ignoreOrientationRequest is false + mDisplayContent.setFocusedApp(secondActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); } @Test - public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() { + public void testCanSpecifyOrientationNoSensor() { final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", @@ -455,34 +495,51 @@ public class TaskDisplayAreaTests extends WindowTestsBase { firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request + final int testOrientation = SCREEN_ORIENTATION_NOSENSOR; + + // ignoreOrientationRequest is always false for SCREEN_ORIENTATION_NOSENSOR so + // only the TDAs with focus can specify orientations mDisplayContent.setFocusedApp(firstActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); - // Activity on TDA2 is focused, and TDA2 respects orientation request mDisplayContent.setFocusedApp(secondActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); } @Test - public void testIgnoreOrientationRequest() { - final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task task = taskDisplayArea.createRootTask( + public void testCanSpecifyOrientationLocked() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstRootTask = firstTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstRootTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondRootTask).build(); + firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - mDisplayContent.setFocusedApp(activity); - activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + final int testOrientation = SCREEN_ORIENTATION_LOCKED; - assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + // ignoreOrientationRequest is always false for SCREEN_ORIENTATION_NOSENSOR so + // only the TDAs with focus can specify orientations + mDisplayContent.setFocusedApp(firstActivity); - taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + + mDisplayContent.setFocusedApp(secondActivity); - assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index db65f49465bc..140051d5c9b1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -247,6 +247,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.dispatchPendingEvents(); + assertTrue(mTaskFragment.mTaskFragmentVanishedSent); assertTaskFragmentVanishedTransaction(); } @@ -259,10 +260,12 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.dispatchPendingEvents(); + assertTrue(mTaskFragment.mTaskFragmentVanishedSent); assertTaskFragmentVanishedTransaction(); // Not trigger onTaskFragmentInfoChanged. // Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged. + mTaskFragment.mTaskFragmentVanishedSent = false; mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.dispatchPendingEvents(); clearInvocations(mOrganizer); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 11ac929327b9..c8932550d877 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -38,6 +38,7 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -98,12 +99,25 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test - public void testOnConfigurationChanged_updateSurface() { - final Rect bounds = new Rect(100, 100, 1100, 1100); + public void testOnConfigurationChanged() { + final Configuration parentConfig = mTaskFragment.getParent().getConfiguration(); + final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); + parentConfig.smallestScreenWidthDp += 10; + final int parentSw = parentConfig.smallestScreenWidthDp; + final Rect bounds = new Rect(parentBounds); + bounds.inset(100, 100); mTaskFragment.setBounds(bounds); + mTaskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + // Calculate its own sw with smaller bounds in multi-window mode. + assertNotEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp); - verify(mTransaction).setPosition(mLeash, 100, 100); - verify(mTransaction).setWindowCrop(mLeash, 1000, 1000); + verify(mTransaction).setPosition(mLeash, bounds.left, bounds.top); + verify(mTransaction).setWindowCrop(mLeash, bounds.width(), bounds.height()); + + mTaskFragment.setBounds(parentBounds); + mTaskFragment.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + // Inherit parent's sw in fullscreen mode. + assertEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp); } @Test 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 0c8e89a8d010..eac67770cf36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -57,6 +57,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; @@ -392,12 +393,16 @@ public class TaskTests extends WindowTestsBase { leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME); leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD); + // We need to use an orientation that is not an exception for the + // ignoreOrientationRequest flag. + final int orientation = SCREEN_ORIENTATION_PORTRAIT; + assertEquals(leafTask2, rootTask.getTopChild()); - assertTrue(rootTask.handlesOrientationChangeFromDescendant()); + assertTrue(rootTask.handlesOrientationChangeFromDescendant(orientation)); // Treat orientation request from home as handled. - assertTrue(leafTask1.handlesOrientationChangeFromDescendant()); + assertTrue(leafTask1.handlesOrientationChangeFromDescendant(orientation)); // Orientation request from standard activity in multi window will not be handled. - assertFalse(leafTask2.handlesOrientationChangeFromDescendant()); + assertFalse(leafTask2.handlesOrientationChangeFromDescendant(orientation)); } @Test @@ -636,7 +641,8 @@ public class TaskTests extends WindowTestsBase { doReturn(parentWindowContainer).when(task).getParent(); doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea(); doReturn(rootTask).when(task).getRootTask(); - doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant(); + doReturn(true).when(parentWindowContainer) + .handlesOrientationChangeFromDescendant(anyInt()); // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the // bounds because its parent says it will handle it at a later time. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index ef3ddb7b1302..ed7d12384662 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -662,13 +662,13 @@ public class WindowContainerTests extends WindowTestsBase { public void testSetOrientation() { final TestWindowContainer root = spy(new TestWindowContainerBuilder(mWm).build()); final TestWindowContainer child = spy(root.addChildWindow()); - doReturn(true).when(root).handlesOrientationChangeFromDescendant(); + doReturn(true).when(root).handlesOrientationChangeFromDescendant(anyInt()); child.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); child.setOrientation(SCREEN_ORIENTATION_PORTRAIT); // The ancestor should decide whether to dispatch the configuration change. verify(child, never()).onConfigurationChanged(any()); - doReturn(false).when(root).handlesOrientationChangeFromDescendant(); + doReturn(false).when(root).handlesOrientationChangeFromDescendant(anyInt()); child.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); // The ancestor doesn't handle the request so the descendant applies the change directly. verify(child).onConfigurationChanged(any()); @@ -843,11 +843,14 @@ public class WindowContainerTests extends WindowTestsBase { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); final TestWindowContainer root = spy(builder.build()); + // We use an orientation that is not an exception for the ignoreOrientationRequest flag + final int orientation = SCREEN_ORIENTATION_PORTRAIT; + final TestWindowContainer child = root.addChildWindow(); - assertFalse(child.handlesOrientationChangeFromDescendant()); + assertFalse(child.handlesOrientationChangeFromDescendant(orientation)); - Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(); - assertTrue(child.handlesOrientationChangeFromDescendant()); + Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(anyInt()); + assertTrue(child.handlesOrientationChangeFromDescendant(orientation)); } @Test diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java index b9d2ae6b0d39..244b3a013056 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java @@ -19,13 +19,13 @@ package com.android.server.voiceinteraction; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.service.voice.HotwordAudioStream.KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_CLOSE_ERROR_FROM_SYSTEM; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_EMPTY_AUDIO_STREAM_LIST; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_END; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_ILLEGAL_COPY_BUFFER_SIZE; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_INTERRUPTED_EXCEPTION; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_NO_PERMISSION; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_START; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__CLOSE_ERROR_FROM_SYSTEM; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__EMPTY_AUDIO_STREAM_LIST; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ENDED; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ILLEGAL_COPY_BUFFER_SIZE; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__INTERRUPTED_EXCEPTION; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__NO_PERMISSION; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__STARTED; import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG; import android.annotation.NonNull; @@ -60,10 +60,10 @@ final class HotwordAudioStreamCopier { private static final String OP_MESSAGE = "Streaming hotword audio to VoiceInteractionService"; private static final String TASK_ID_PREFIX = "HotwordDetectedResult@"; private static final String THREAD_NAME_PREFIX = "Copy-"; - private static final int DEFAULT_COPY_BUFFER_LENGTH_BYTES = 2_560; // Corresponds to the OS pipe capacity in bytes private static final int MAX_COPY_BUFFER_LENGTH_BYTES = 65_536; + private static final int DEFAULT_COPY_BUFFER_LENGTH_BYTES = 32_768; private final AppOpsManager mAppOpsManager; private final int mDetectorType; @@ -98,14 +98,17 @@ final class HotwordAudioStreamCopier { throws IOException { List<HotwordAudioStream> audioStreams = result.getAudioStreams(); if (audioStreams.isEmpty()) { - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_EMPTY_AUDIO_STREAM_LIST, - mVoiceInteractorUid); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__EMPTY_AUDIO_STREAM_LIST, + mVoiceInteractorUid, /* streamSizeBytes= */ 0, /* bundleSizeBytes= */ 0, + /* streamCount= */ 0); return result; } + final int audioStreamCount = audioStreams.size(); List<HotwordAudioStream> newAudioStreams = new ArrayList<>(audioStreams.size()); List<CopyTaskInfo> copyTaskInfos = new ArrayList<>(audioStreams.size()); + int totalMetadataBundleSizeBytes = 0; for (HotwordAudioStream audioStream : audioStreams) { ParcelFileDescriptor[] clientPipe = ParcelFileDescriptor.createReliablePipe(); ParcelFileDescriptor clientAudioSource = clientPipe[0]; @@ -117,12 +120,14 @@ final class HotwordAudioStreamCopier { int copyBufferLength = DEFAULT_COPY_BUFFER_LENGTH_BYTES; PersistableBundle metadata = audioStream.getMetadata(); + totalMetadataBundleSizeBytes += HotwordDetectedResult.getParcelableSize(metadata); if (metadata.containsKey(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES)) { copyBufferLength = metadata.getInt(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES, -1); if (copyBufferLength < 1 || copyBufferLength > MAX_COPY_BUFFER_LENGTH_BYTES) { - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_ILLEGAL_COPY_BUFFER_SIZE, - mVoiceInteractorUid); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ILLEGAL_COPY_BUFFER_SIZE, + mVoiceInteractorUid, /* streamSizeBytes= */ 0, /* bundleSizeBytes= */ 0, + audioStreamCount); Slog.w(TAG, "Attempted to set an invalid copy buffer length (" + copyBufferLength + ") for: " + audioStream); copyBufferLength = DEFAULT_COPY_BUFFER_LENGTH_BYTES; @@ -139,7 +144,9 @@ final class HotwordAudioStreamCopier { } String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result); - mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, copyTaskInfos)); + mExecutorService.execute( + new HotwordDetectedResultCopyTask(resultTaskId, copyTaskInfos, + totalMetadataBundleSizeBytes)); return result.buildUpon().setAudioStreams(newAudioStreams).build(); } @@ -159,11 +166,14 @@ final class HotwordAudioStreamCopier { private class HotwordDetectedResultCopyTask implements Runnable { private final String mResultTaskId; private final List<CopyTaskInfo> mCopyTaskInfos; + private final int mTotalMetadataSizeBytes; private final ExecutorService mExecutorService = Executors.newCachedThreadPool(); - HotwordDetectedResultCopyTask(String resultTaskId, List<CopyTaskInfo> copyTaskInfos) { + HotwordDetectedResultCopyTask(String resultTaskId, List<CopyTaskInfo> copyTaskInfos, + int totalMetadataSizeBytes) { mResultTaskId = resultTaskId; mCopyTaskInfos = copyTaskInfos; + mTotalMetadataSizeBytes = totalMetadataSizeBytes; } @Override @@ -183,19 +193,38 @@ final class HotwordAudioStreamCopier { mVoiceInteractorUid, mVoiceInteractorPackageName, mVoiceInteractorAttributionTag, OP_MESSAGE) == MODE_ALLOWED) { try { - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_START, - mVoiceInteractorUid); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__STARTED, + mVoiceInteractorUid, /* streamSizeBytes= */ 0, mTotalMetadataSizeBytes, + size); // TODO(b/244599891): Set timeout, close after inactivity mExecutorService.invokeAll(tasks); - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_END, - mVoiceInteractorUid); + + int totalStreamSizeBytes = 0; + for (SingleAudioStreamCopyTask task : tasks) { + totalStreamSizeBytes += task.mTotalCopiedBytes; + } + + Slog.i(TAG, mResultTaskId + ": Task was completed. Total bytes streamed: " + + totalStreamSizeBytes + ", total metadata bundle size bytes: " + + mTotalMetadataSizeBytes); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ENDED, + mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes, + size); } catch (InterruptedException e) { - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_INTERRUPTED_EXCEPTION, - mVoiceInteractorUid); - Slog.e(TAG, mResultTaskId + ": Task was interrupted", e); + int totalStreamSizeBytes = 0; + for (SingleAudioStreamCopyTask task : tasks) { + totalStreamSizeBytes += task.mTotalCopiedBytes; + } + + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__INTERRUPTED_EXCEPTION, + mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes, + size); + Slog.e(TAG, mResultTaskId + ": Task was interrupted. Total bytes streamed: " + + totalStreamSizeBytes + ", total metadata bundle size bytes: " + + mTotalMetadataSizeBytes); bestEffortPropagateError(e.getMessage()); } finally { mAppOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD, @@ -203,9 +232,10 @@ final class HotwordAudioStreamCopier { mVoiceInteractorAttributionTag); } } else { - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_NO_PERMISSION, - mVoiceInteractorUid); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__NO_PERMISSION, + mVoiceInteractorUid, /* streamSizeBytes= */ 0, /* bundleSizeBytes= */ 0, + size); bestEffortPropagateError( "Failed to obtain RECORD_AUDIO_HOTWORD permission for voice interactor with" + " uid=" + mVoiceInteractorUid @@ -220,9 +250,10 @@ final class HotwordAudioStreamCopier { copyTaskInfo.mSource.closeWithError(errorMessage); copyTaskInfo.mSink.closeWithError(errorMessage); } - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_CLOSE_ERROR_FROM_SYSTEM, - mVoiceInteractorUid); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__CLOSE_ERROR_FROM_SYSTEM, + mVoiceInteractorUid, /* streamSizeBytes= */ 0, /* bundleSizeBytes= */ 0, + mCopyTaskInfos.size()); } catch (IOException e) { Slog.e(TAG, mResultTaskId + ": Failed to propagate error", e); } @@ -237,6 +268,8 @@ final class HotwordAudioStreamCopier { private final int mDetectorType; private final int mUid; + private volatile int mTotalCopiedBytes = 0; + SingleAudioStreamCopyTask(String streamTaskId, ParcelFileDescriptor audioSource, ParcelFileDescriptor audioSink, int copyBufferLength, int detectorType, int uid) { mStreamTaskId = streamTaskId; @@ -281,6 +314,7 @@ final class HotwordAudioStreamCopier { Arrays.copyOfRange(buffer, 0, 20))); } fos.write(buffer, 0, bytesRead); + mTotalCopiedBytes += bytesRead; } // TODO(b/244599891): Close PFDs after inactivity } @@ -288,8 +322,10 @@ final class HotwordAudioStreamCopier { mAudioSource.closeWithError(e.getMessage()); mAudioSink.closeWithError(e.getMessage()); Slog.e(TAG, mStreamTaskId + ": Failed to copy audio stream", e); - HotwordMetricsLogger.writeDetectorEvent(mDetectorType, - HOTWORD_DETECTOR_EVENTS__EVENT__AUDIO_EGRESS_CLOSE_ERROR_FROM_SYSTEM, mUid); + HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType, + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__CLOSE_ERROR_FROM_SYSTEM, + mUid, /* streamSizeBytes= */ 0, /* bundleSizeBytes= */ 0, + /* streamCount= */ 0); } finally { if (fis != null) { fis.close(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 7074b19cecd0..55bf2ab3a0e9 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -268,7 +268,8 @@ final class HotwordDetectionConnection { synchronized (mLock) { restartProcessLocked(); HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType, - HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE); + HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE, + mVoiceInteractionServiceUid); } }, mReStartPeriodSeconds, mReStartPeriodSeconds, TimeUnit.SECONDS); } @@ -303,7 +304,8 @@ final class HotwordDetectionConnection { // conditions with audio reading in the service. restartProcessLocked(); HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType, - HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED); + HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED, + mVoiceInteractionServiceUid); } } @@ -334,13 +336,14 @@ final class HotwordDetectionConnection { try { mCallback.onStatusReported(status); HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType, - initResultMetricsResult); + initResultMetricsResult, mVoiceInteractionServiceUid); } catch (RemoteException e) { // TODO: Add a new atom for RemoteException case, the error doesn't very // correct here Slog.w(TAG, "Failed to report initialization status: " + e); HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType, - METRICS_INIT_CALLBACK_STATE_ERROR); + METRICS_INIT_CALLBACK_STATE_ERROR, + mVoiceInteractionServiceUid); } } }; @@ -363,11 +366,12 @@ final class HotwordDetectionConnection { try { mCallback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN); HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType, - METRICS_INIT_UNKNOWN_TIMEOUT); + METRICS_INIT_UNKNOWN_TIMEOUT, mVoiceInteractionServiceUid); } catch (RemoteException e) { Slog.w(TAG, "Failed to report initialization status UNKNOWN", e); HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType, - METRICS_INIT_CALLBACK_STATE_ERROR); + METRICS_INIT_CALLBACK_STATE_ERROR, + mVoiceInteractionServiceUid); } } else if (err != null) { Slog.w(TAG, "Failed to update state: " + err); @@ -470,12 +474,14 @@ final class HotwordDetectionConnection { synchronized (mLock) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED, + mVoiceInteractionServiceUid); if (!mPerformingSoftwareHotwordDetection) { Slog.i(TAG, "Hotword detection has already completed"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK); + METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK, + mVoiceInteractionServiceUid); return; } mPerformingSoftwareHotwordDetection = false; @@ -484,7 +490,8 @@ final class HotwordDetectionConnection { } catch (SecurityException e) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION, + mVoiceInteractionServiceUid); mSoftwareCallback.onError(); return; } @@ -513,7 +520,8 @@ final class HotwordDetectionConnection { } HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED, + mVoiceInteractionServiceUid); // onRejected isn't allowed here, and we are not expecting it. } }; @@ -661,12 +669,14 @@ final class HotwordDetectionConnection { } HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED, + mVoiceInteractionServiceUid); if (!mValidatingDspTrigger) { Slog.i(TAG, "Ignoring #onDetected due to a process restart"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK); + METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK, + mVoiceInteractionServiceUid); return; } mValidatingDspTrigger = false; @@ -676,7 +686,8 @@ final class HotwordDetectionConnection { Slog.i(TAG, "Ignoring #onDetected due to a SecurityException", e); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION, + mVoiceInteractionServiceUid); externalCallback.onError(CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION); return; } @@ -709,12 +720,14 @@ final class HotwordDetectionConnection { } HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED, + mVoiceInteractionServiceUid); if (!mValidatingDspTrigger) { Slog.i(TAG, "Ignoring #onRejected due to a process restart"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK); + METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK, + mVoiceInteractionServiceUid); return; } mValidatingDspTrigger = false; @@ -728,21 +741,20 @@ final class HotwordDetectionConnection { synchronized (mLock) { mValidatingDspTrigger = true; - mRemoteHotwordDetectionService.run( - service -> { - // TODO: avoid allocate every time - mCancellationKeyPhraseDetectionFuture = mScheduledExecutorService.schedule( - () -> HotwordMetricsLogger - .writeKeyphraseTriggerEvent(mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT), - VALIDATION_TIMEOUT_MILLIS, - TimeUnit.MILLISECONDS); - service.detectFromDspSource( - recognitionEvent, - recognitionEvent.getCaptureFormat(), - VALIDATION_TIMEOUT_MILLIS, - internalCallback); - }); + mRemoteHotwordDetectionService.run(service -> { + // TODO: avoid allocate every time + mCancellationKeyPhraseDetectionFuture = mScheduledExecutorService.schedule( + () -> HotwordMetricsLogger.writeKeyphraseTriggerEvent(mDetectorType, + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT, + mVoiceInteractionServiceUid), + VALIDATION_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + service.detectFromDspSource( + recognitionEvent, + recognitionEvent.getCaptureFormat(), + VALIDATION_TIMEOUT_MILLIS, + internalCallback); + }); } } @@ -790,7 +802,8 @@ final class HotwordDetectionConnection { mCallback.onRejected(new HotwordRejectedResult.Builder().build()); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART, + mVoiceInteractionServiceUid); } catch (RemoteException e) { Slog.w(TAG, "Failed to call #rejected"); } @@ -836,11 +849,13 @@ final class HotwordDetectionConnection { private SoundTrigger.KeyphraseRecognitionEvent mRecognitionEvent; private final HotwordDetectionConnection mHotwordDetectionConnection; private final IHotwordRecognitionStatusCallback mExternalCallback; + private final int mVoiceInteractionServiceUid; SoundTriggerCallback(IHotwordRecognitionStatusCallback callback, - HotwordDetectionConnection connection) { + HotwordDetectionConnection connection, int uid) { mHotwordDetectionConnection = connection; mExternalCallback = callback; + mVoiceInteractionServiceUid = uid; } @Override @@ -853,14 +868,16 @@ final class HotwordDetectionConnection { if (useHotwordDetectionService) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER, + mVoiceInteractionServiceUid); mRecognitionEvent = recognitionEvent; mHotwordDetectionConnection.detectFromDspSource( recognitionEvent, mExternalCallback); } else { HotwordMetricsLogger.writeKeyphraseTriggerEvent( HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER); + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER, + mVoiceInteractionServiceUid); mExternalCallback.onKeyphraseDetected(recognitionEvent, null); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java index 940aed34b7fb..c35d90f4a495 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java @@ -16,6 +16,9 @@ package com.android.server.voiceinteraction; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__DETECTOR_TYPE__NORMAL_DETECTOR; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__NORMAL_DETECTOR; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE; @@ -47,6 +50,12 @@ public final class HotwordMetricsLogger { HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; private static final int METRICS_INIT_NORMAL_DETECTOR = HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__NORMAL_DETECTOR; + private static final int AUDIO_EGRESS_DSP_DETECTOR = + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; + private static final int AUDIO_EGRESS_SOFTWARE_DETECTOR = + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE; + private static final int AUDIO_EGRESS_NORMAL_DETECTOR = + HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__DETECTOR_TYPE__NORMAL_DETECTOR; private HotwordMetricsLogger() { // Class only contains static utility functions, and should not be instantiated @@ -64,28 +73,28 @@ public final class HotwordMetricsLogger { /** * Logs information related to hotword detection service init result. */ - public static void writeServiceInitResultEvent(int detectorType, int result) { + public static void writeServiceInitResultEvent(int detectorType, int result, int uid) { int metricsDetectorType = getInitMetricsDetectorType(detectorType); FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED, - metricsDetectorType, result); + metricsDetectorType, result, uid); } /** * Logs information related to hotword detection service restarting. */ - public static void writeServiceRestartEvent(int detectorType, int reason) { + public static void writeServiceRestartEvent(int detectorType, int reason, int uid) { int metricsDetectorType = getRestartMetricsDetectorType(detectorType); FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED, - metricsDetectorType, reason); + metricsDetectorType, reason, uid); } /** * Logs information related to keyphrase trigger. */ - public static void writeKeyphraseTriggerEvent(int detectorType, int result) { + public static void writeKeyphraseTriggerEvent(int detectorType, int result, int uid) { int metricsDetectorType = getKeyphraseMetricsDetectorType(detectorType); FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED, - metricsDetectorType, result); + metricsDetectorType, result, uid); } /** @@ -97,6 +106,16 @@ public final class HotwordMetricsLogger { metricsDetectorType, event, uid); } + /** + * Logs information related to hotword audio egress events. + */ + public static void writeAudioEgressEvent(int detectorType, int event, int uid, + int streamSizeBytes, int bundleSizeBytes, int streamCount) { + int metricsDetectorType = getAudioEgressDetectorType(detectorType); + FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_AUDIO_EGRESS_EVENT_REPORTED, + metricsDetectorType, event, uid, streamSizeBytes, bundleSizeBytes, streamCount); + } + private static int getCreateMetricsDetectorType(int detectorType) { switch (detectorType) { case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE: @@ -151,4 +170,15 @@ public final class HotwordMetricsLogger { return HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__NORMAL_DETECTOR; } } + + private static int getAudioEgressDetectorType(int detectorType) { + switch (detectorType) { + case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE: + return AUDIO_EGRESS_SOFTWARE_DETECTOR; + case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP: + return AUDIO_EGRESS_DSP_DETECTOR; + default: + return AUDIO_EGRESS_NORMAL_DETECTOR; + } + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 2f92bb3baa9b..c4f341e951da 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -711,7 +711,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne Slog.d(TAG, "createSoundTriggerCallbackLocked"); } return new HotwordDetectionConnection.SoundTriggerCallback(callback, - mHotwordDetectionConnection); + mHotwordDetectionConnection, mInfo.getServiceInfo().applicationInfo.uid); } private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName, diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt index 2031af2cf0c9..1930a1c8bbb2 100644 --- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt +++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt @@ -63,6 +63,7 @@ class LockStateTrackingRule : TestRule { inner class Listener : TrustListener { override fun onTrustChanged( enabled: Boolean, + newlyUnlocked: Boolean, userId: Int, flags: Int, trustGrantedMessages: MutableList<String> |