diff options
682 files changed, 9944 insertions, 4220 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 20471682dd2e..7317ecd82bd3 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -310,6 +310,8 @@ java_aconfig_library { aconfig_declarations { name: "android.os.flags-aconfig", package: "android.os", + exportable: true, + container: "system", srcs: ["core/java/android/os/*.aconfig"], } @@ -326,6 +328,13 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +java_aconfig_library { + name: "android.os.flags-aconfig-java-export", + aconfig_declarations: "android.os.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], + mode: "exported", +} + // VirtualDeviceManager cc_aconfig_library { name: "android.companion.virtualdevice.flags-aconfig-cc", @@ -483,6 +492,13 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +java_aconfig_library { + name: "android.content.res.flags-aconfig-java-host", + aconfig_declarations: "android.content.res.flags-aconfig", + host_supported: true, + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Media BetterTogether aconfig_declarations { name: "com.android.media.flags.bettertogether-aconfig", diff --git a/Android.bp b/Android.bp index 5ada10d19f5d..8d7ab983593d 100644 --- a/Android.bp +++ b/Android.bp @@ -386,6 +386,7 @@ java_defaults { // TODO(b/120066492): remove gps_debug and protolog.conf.json when the build // system propagates "required" properly. "gps_debug.conf", + "protolog.conf.json.gz", "core.protolog.pb", "framework-res", // any install dependencies should go into framework-minus-apex-install-dependencies diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index a7560b23c6bd..12b79f4c42f8 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -23,8 +23,6 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; -import android.sysprop.InitProperties; public class PowerCommand extends Svc.Command { private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0; @@ -142,12 +140,10 @@ public class PowerCommand extends Svc.Command { // Check if remote exception is benign during shutdown. Pm can be killed // before system server during shutdown, so remote exception can be ignored // if it is already in shutdown flow. + // sys.powerctl is no longer set to avoid a possible DOS attack (see + // bionic/libc/bionic/system_property_set.cpp) so we have no real way of knowing if a + // remote exception is real or simply because pm is killed (b/318323013) + // So we simply do not display anything. private void maybeLogRemoteException(String msg) { - String powerProp = SystemProperties.get("sys.powerctl"); - // Also check if userspace reboot is ongoing, since in case of userspace reboot value of the - // sys.powerctl property will be reset. - if (powerProp.isEmpty() && !InitProperties.userspace_reboot_in_progress().orElse(false)) { - System.err.println(msg); - } } } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2922dd9ef5ae..afb796b7687a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10758,7 +10758,7 @@ package android.os { method public final double readDouble(); method public final java.util.ArrayList<java.lang.Double> readDoubleVector(); method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean); - method @NonNull @Nullable public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long); + method @NonNull public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long); method @Nullable public final android.os.NativeHandle readEmbeddedNativeHandle(long, long); method public final float readFloat(); method public final java.util.ArrayList<java.lang.Float> readFloatVector(); @@ -12963,9 +12963,22 @@ package android.service.ondeviceintelligence { method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>); method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer); method public abstract void onListFeatures(@NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); + method public final void updateProcessingState(@NonNull android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException>); field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService"; } + public static class OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException extends java.lang.Exception { + ctor public OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException(int); + ctor public OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException(int, @NonNull String); + method public int getErrorCode(); + } + + public static class OnDeviceIntelligenceService.OnDeviceUpdateProcessingException extends android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException { + ctor public OnDeviceIntelligenceService.OnDeviceUpdateProcessingException(int); + ctor public OnDeviceIntelligenceService.OnDeviceUpdateProcessingException(int, @NonNull String); + field public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 1; // 0x1 + } + @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceTrustedInferenceService extends android.app.Service { ctor public OnDeviceTrustedInferenceService(); method public final void fetchFeatureFileInputStreamMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.io.FileInputStream>>); @@ -12973,6 +12986,7 @@ package android.service.ondeviceintelligence { method @NonNull public abstract void onCountTokens(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); method @NonNull public abstract void onProcessRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); method @NonNull public abstract void onProcessRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); + method public abstract void onUpdateProcessingState(@NonNull android.os.Bundle, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException>); method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException; method public final void openFileInputAsync(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.io.FileInputStream>) throws java.io.FileNotFoundException; field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index a28dc497c508..6fb6b0d1d5bf 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1366,7 +1366,7 @@ package android.credentials.selection { } @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class IntentFactory { - method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.os.IBinder, boolean, @NonNull String); + method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.content.Context, @NonNull android.os.IBinder, boolean, @NonNull String); method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.content.Context, @NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver); } @@ -1547,10 +1547,6 @@ package android.hardware.biometrics { method public boolean isAllowBackgroundAuthentication(); } - public abstract static class BiometricPrompt.AuthenticationCallback { - method @FlaggedApi("android.hardware.biometrics.face_background_authentication") public void onAuthenticationAcquired(int); - } - public static class BiometricPrompt.Builder { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean); method @FlaggedApi("android.multiuser.enable_biometrics_to_unlock_private_space") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean, boolean); @@ -1569,7 +1565,6 @@ package android.hardware.biometrics { } public class SensorProperties { - ctor @FlaggedApi("android.hardware.biometrics.face_background_authentication") public SensorProperties(int, int, @NonNull java.util.List<android.hardware.biometrics.SensorProperties.ComponentInfo>); method @NonNull public java.util.List<android.hardware.biometrics.SensorProperties.ComponentInfo> getComponentInfo(); method public int getSensorId(); method public int getSensorStrength(); @@ -1714,18 +1709,6 @@ package android.hardware.display { } -package android.hardware.face { - - @FlaggedApi("android.hardware.biometrics.face_background_authentication") public class FaceManager { - method @FlaggedApi("android.hardware.biometrics.face_background_authentication") @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int); - method @FlaggedApi("android.hardware.biometrics.face_background_authentication") @NonNull public java.util.List<android.hardware.face.FaceSensorProperties> getSensorProperties(); - } - - @FlaggedApi("android.hardware.biometrics.face_background_authentication") public class FaceSensorProperties extends android.hardware.biometrics.SensorProperties { - } - -} - package android.hardware.fingerprint { @Deprecated public class FingerprintManager { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 41151c0dc647..1d39186de183 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -40,6 +40,7 @@ import static android.window.ConfigurationHelper.shouldUpdateResources; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext; +import static com.android.window.flags.Flags.activityWindowInfoFlag; import android.annotation.NonNull; import android.annotation.Nullable; @@ -63,6 +64,7 @@ import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ActivityResultItem; import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.ClientTransactionListenerController; import android.app.servertransaction.DestroyActivityItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.PendingTransactionActions; @@ -606,6 +608,8 @@ public final class ActivityThread extends ClientTransactionHandler Configuration overrideConfig; @NonNull private ActivityWindowInfo mActivityWindowInfo; + @Nullable + private ActivityWindowInfo mLastReportedActivityWindowInfo; // Used for consolidating configs before sending on to Activity. private final Configuration tmpConfig = new Configuration(); @@ -4180,6 +4184,9 @@ public final class ActivityThread extends ClientTransactionHandler pendingActions.setRestoreInstanceState(true); pendingActions.setCallOnPostCreate(true); } + + // Trigger ActivityWindowInfo callback if first launch or change from relaunch. + handleActivityWindowInfoChanged(r); } else { // If there was an error, for any reason, tell the activity manager to stop us. ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED, @@ -4558,7 +4565,7 @@ public final class ActivityThread extends ClientTransactionHandler private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) { final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token, - r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags, + r.activity.isFinishing(), /* userLeaving */ true, /* dontReport */ false, /* autoEnteringPip */ false); transaction.addTransactionItem(pauseActivityItem); executeTransaction(transaction); @@ -5432,13 +5439,12 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving, - int configChanges, boolean autoEnteringPip, PendingTransactionActions pendingActions, + boolean autoEnteringPip, PendingTransactionActions pendingActions, String reason) { if (userLeaving) { performUserLeavingActivity(r); } - r.activity.mConfigChangeFlags |= configChanges; if (autoEnteringPip) { // Set mIsInPictureInPictureMode earlier in case of auto-enter-pip, see also // {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}. @@ -5687,9 +5693,8 @@ public final class ActivityThread extends ClientTransactionHandler } @Override - public void handleStopActivity(ActivityClientRecord r, int configChanges, + public void handleStopActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { - r.activity.mConfigChangeFlags |= configChanges; final StopInfo stopInfo = new StopInfo(); performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest, @@ -5859,11 +5864,10 @@ public final class ActivityThread extends ClientTransactionHandler /** Core implementation of activity destroy call. */ void performDestroyActivity(ActivityClientRecord r, boolean finishing, - int configChanges, boolean getNonConfigInstance, String reason) { + boolean getNonConfigInstance, String reason) { Class<? extends Activity> activityClass; if (localLOGV) Slog.v(TAG, "Performing finish of " + r); activityClass = r.activity.getClass(); - r.activity.mConfigChangeFlags |= configChanges; if (finishing) { r.activity.mFinished = true; } @@ -5928,9 +5932,9 @@ public final class ActivityThread extends ClientTransactionHandler } @Override - public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, + public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, boolean getNonConfigInstance, String reason) { - performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason); + performDestroyActivity(r, finishing, getNonConfigInstance, reason); cleanUpPendingRemoveWindows(r, finishing); WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; @@ -6130,7 +6134,7 @@ public final class ActivityThread extends ClientTransactionHandler r.activity.mChangingConfigurations = true; - handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, + handleRelaunchActivityInner(r, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo, "handleRelaunchActivity"); } @@ -6199,7 +6203,7 @@ public final class ActivityThread extends ClientTransactionHandler executeTransaction(transaction); } - private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, int configChanges, + private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, @Nullable List<ResultInfo> pendingResults, @Nullable List<ReferrerIntent> pendingIntents, @NonNull PendingTransactionActions pendingActions, boolean startsNotResumed, @@ -6215,7 +6219,7 @@ public final class ActivityThread extends ClientTransactionHandler callActivityOnStop(r, true /* saveState */, reason); } - handleDestroyActivity(r, false, configChanges, true, reason); + handleDestroyActivity(r, false /* finishing */, true /* getNonConfigInstance */, reason); r.activity = null; r.window = null; @@ -6740,7 +6744,7 @@ public final class ActivityThread extends ClientTransactionHandler // Perform updates. r.overrideConfig = overrideConfig; r.mActivityWindowInfo = activityWindowInfo; - // TODO(b/287582673): notify on ActivityWindowInfo change + final ViewRootImpl viewRoot = r.activity.mDecor != null ? r.activity.mDecor.getViewRootImpl() : null; @@ -6763,6 +6767,22 @@ public final class ActivityThread extends ClientTransactionHandler viewRoot.updateConfiguration(displayId); } mSomeActivitiesChanged = true; + + // Trigger ActivityWindowInfo callback if changed. + handleActivityWindowInfoChanged(r); + } + + private void handleActivityWindowInfoChanged(@NonNull ActivityClientRecord r) { + if (!activityWindowInfoFlag()) { + return; + } + if (r.mActivityWindowInfo == null + || r.mActivityWindowInfo.equals(r.mLastReportedActivityWindowInfo)) { + return; + } + r.mLastReportedActivityWindowInfo = r.mActivityWindowInfo; + ClientTransactionListenerController.getInstance().onActivityWindowInfoChanged(r.token, + r.mActivityWindowInfo); } final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index b5b3669c1d80..01153c9e7efc 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -114,11 +114,11 @@ public abstract class ClientTransactionHandler { /** Destroy the activity. */ public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing, - int configChanges, boolean getNonConfigInstance, String reason); + boolean getNonConfigInstance, String reason); /** Pause the activity. */ public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished, - boolean userLeaving, int configChanges, boolean autoEnteringPip, + boolean userLeaving, boolean autoEnteringPip, PendingTransactionActions pendingActions, String reason); /** @@ -146,14 +146,13 @@ public abstract class ClientTransactionHandler { /** * Stop the activity. * @param r Target activity record. - * @param configChanges Activity configuration changes. * @param pendingActions Pending actions to be used on this or later stages of activity * transaction. * @param finalStateRequest Flag indicating if this call is handling final lifecycle state * request for a transaction. * @param reason Reason for performing this operation. */ - public abstract void handleStopActivity(@NonNull ActivityClientRecord r, int configChanges, + public abstract void handleStopActivity(@NonNull ActivityClientRecord r, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason); /** Report that activity was stopped to server. */ diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java index 1b19ecdd5931..095cfc5a7303 100644 --- a/core/java/android/app/LocalActivityManager.java +++ b/core/java/android/app/LocalActivityManager.java @@ -413,7 +413,7 @@ public class LocalActivityManager { if (localLOGV) Log.v(TAG, r.id + ": destroying"); final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r); if (clientRecord != null) { - mActivityThread.performDestroyActivity(clientRecord, finish, 0 /* configChanges */, + mActivityThread.performDestroyActivity(clientRecord, finish, false /* getNonConfigInstance */, "LocalActivityManager::performDestroy"); } r.activity = null; @@ -684,7 +684,7 @@ public class LocalActivityManager { if (localLOGV) Log.v(TAG, r.id + ": no corresponding record"); continue; } - mActivityThread.performDestroyActivity(clientRecord, finishing, 0 /* configChanges */, + mActivityThread.performDestroyActivity(clientRecord, finishing, false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy"); } mActivities.clear(); diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java index b2be27f70ccc..4a06f7d1a1c3 100644 --- a/core/java/android/app/LocaleConfig.java +++ b/core/java/android/app/LocaleConfig.java @@ -248,7 +248,8 @@ public class LocaleConfig implements Parcelable { } /** - * Returns the default locale if specified, otherwise null + * Returns the locale the strings in values/strings.xml (the default strings in the directory + * with no locale qualifier) are in if specified, otherwise null * * @return The default Locale or null */ diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index d49a2542eed8..283e21aab39c 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -2052,10 +2052,12 @@ public class NotificationManager { /** Notification senders to prioritize for calls. One of: * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ + @PrioritySenders public final int priorityCallSenders; /** Notification senders to prioritize for messages. One of: * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ + @PrioritySenders public final int priorityMessageSenders; /** @@ -2063,6 +2065,7 @@ public class NotificationManager { * {@link #CONVERSATION_SENDERS_NONE}, {@link #CONVERSATION_SENDERS_IMPORTANT}, * {@link #CONVERSATION_SENDERS_ANYONE}. */ + @ConversationSenders public final int priorityConversationSenders; /** @@ -2630,16 +2633,19 @@ public class NotificationManager { } /** @hide **/ + @PrioritySenders public int allowCallsFrom() { return priorityCallSenders; } /** @hide **/ + @PrioritySenders public int allowMessagesFrom() { return priorityMessageSenders; } /** @hide **/ + @ConversationSenders public int allowConversationsFrom() { return priorityConversationSenders; } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 625526047212..8b84f062b7b5 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -124,6 +124,32 @@ public class ResourcesManager { */ private LocaleConfig mLocaleConfig = new LocaleConfig(LocaleList.getEmptyLocaleList()); + private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap = + new ArrayMap<>(); + + /** + * The internal function to register the resources paths of a package (e.g. a shared library). + * This will collect the package resources' paths from its ApplicationInfo and add them to all + * existing and future contexts while the application is running. + */ + public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) { + SharedLibraryAssets sharedLibAssets = new SharedLibraryAssets(appInfo.sourceDir, + appInfo.splitSourceDirs, appInfo.sharedLibraryFiles, + appInfo.resourceDirs, appInfo.overlayPaths); + + synchronized (mLock) { + if (mSharedLibAssetsMap.containsKey(uniqueId)) { + Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId + + " has already been registered, this is a no-op."); + return; + } + mSharedLibAssetsMap.put(uniqueId, sharedLibAssets); + appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths()); + Slog.v(TAG, "The following resources' paths have been added: " + + Arrays.toString(sharedLibAssets.getAllAssetPaths())); + } + } + private static class ApkKey { public final String path; public final boolean sharedLib; @@ -278,6 +304,21 @@ public class ResourcesManager { public ResourcesManager() { } + /** + * Inject a customized ResourcesManager instance for testing, return the old ResourcesManager + * instance. + */ + @UnsupportedAppUsage + @VisibleForTesting + public static ResourcesManager setInstance(ResourcesManager resourcesManager) { + synchronized (ResourcesManager.class) { + ResourcesManager oldResourceManager = sResourcesManager; + sResourcesManager = resourcesManager; + return oldResourceManager; + } + + } + @UnsupportedAppUsage public static ResourcesManager getInstance() { synchronized (ResourcesManager.class) { @@ -1480,6 +1521,56 @@ public class ResourcesManager { } } + private void appendLibAssetsLocked(String[] libAssets) { + synchronized (mLock) { + // Record which ResourcesImpl need updating + // (and what ResourcesKey they should update to). + final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); + + final int implCount = mResourceImpls.size(); + for (int i = 0; i < implCount; i++) { + final ResourcesKey key = mResourceImpls.keyAt(i); + final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); + final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; + if (impl == null) { + Slog.w(TAG, "Found a ResourcesImpl which is null, skip it and continue to " + + "append shared library assets for next ResourcesImpl."); + continue; + } + + var newDirs = new ArrayList<String>(); + var dirsSet = new ArraySet<String>(); + if (key.mLibDirs != null) { + final int dirsLength = key.mLibDirs.length; + for (int k = 0; k < dirsLength; k++) { + newDirs.add(key.mLibDirs[k]); + dirsSet.add(key.mLibDirs[k]); + } + } + final int assetsLength = libAssets.length; + for (int j = 0; j < assetsLength; j++) { + if (dirsSet.add(libAssets[j])) { + newDirs.add(libAssets[j]); + } + } + String[] newLibAssets = newDirs.toArray(new String[0]); + if (!Arrays.equals(newLibAssets, key.mLibDirs)) { + updatedResourceKeys.put(impl, new ResourcesKey( + key.mResDir, + key.mSplitResDirs, + key.mOverlayPaths, + newLibAssets, + key.mDisplayId, + key.mOverrideConfiguration, + key.mCompatInfo, + key.mLoaders)); + } + } + + redirectResourcesToNewImplLocked(updatedResourceKeys); + } + } + private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs, @NonNull final ApplicationInfo appInfo) { try { @@ -1689,4 +1780,50 @@ public class ResourcesManager { } } } + + public static class SharedLibraryAssets{ + private final String[] mAssetPaths; + + SharedLibraryAssets(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles, + String[] resourceDirs, String[] overlayPaths) { + mAssetPaths = collectAssetPaths(sourceDir, splitSourceDirs, sharedLibraryFiles, + resourceDirs, overlayPaths); + } + + private @NonNull String[] collectAssetPaths(String sourceDir, String[] splitSourceDirs, + String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) { + final String[][] inputLists = { + splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths + }; + + final ArraySet<String> assetPathSet = new ArraySet<>(); + final List<String> assetPathList = new ArrayList<>(); + if (sourceDir != null) { + assetPathSet.add(sourceDir); + assetPathList.add(sourceDir); + } + + for (int i = 0; i < inputLists.length; i++) { + if (inputLists[i] != null) { + for (int j = 0; j < inputLists[i].length; j++) { + if (assetPathSet.add(inputLists[i][j])) { + assetPathList.add(inputLists[i][j]); + } + } + } + } + return assetPathList.toArray(new String[0]); + } + + /** + * @return all the asset paths of this collected in this class. + */ + public @NonNull String[] getAllAssetPaths() { + return mAssetPaths; + } + } + + public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() { + return new ArrayMap<>(mSharedLibAssetsMap); + } } diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java index d81eb20512a2..51f313755e59 100644 --- a/core/java/android/app/admin/AccountTypePolicyKey.java +++ b/core/java/android/app/admin/AccountTypePolicyKey.java @@ -19,12 +19,12 @@ package android.app.admin; import static android.app.admin.PolicyUpdateReceiver.EXTRA_ACCOUNT_TYPE; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.admin.flags.Flags; import android.os.Bundle; import android.os.Parcel; @@ -54,7 +54,7 @@ public final class AccountTypePolicyKey extends PolicyKey { @TestApi public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) { super(key); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType"); } mAccountType = Objects.requireNonNull((accountType)); diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java index cc5e75fac0ea..4f7060461091 100644 --- a/core/java/android/app/admin/BundlePolicyValue.java +++ b/core/java/android/app/admin/BundlePolicyValue.java @@ -16,10 +16,9 @@ package android.app.admin; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.flags.Flags; import android.os.Bundle; import android.os.Parcel; @@ -32,7 +31,7 @@ public final class BundlePolicyValue extends PolicyValue<Bundle> { public BundlePolicyValue(Bundle value) { super(value); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxParcelableFieldsLength(value); } } diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java index 4d36195613ad..a957dbf132bb 100644 --- a/core/java/android/app/admin/ComponentNamePolicyValue.java +++ b/core/java/android/app/admin/ComponentNamePolicyValue.java @@ -16,10 +16,9 @@ package android.app.admin; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.flags.Flags; import android.content.ComponentName; import android.os.Parcel; @@ -32,7 +31,7 @@ public final class ComponentNamePolicyValue extends PolicyValue<ComponentName> { public ComponentNamePolicyValue(@NonNull ComponentName value) { super(value); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxComponentNameLength(value); } } diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java index de7ff9f0ad0f..63c3a4cb499f 100644 --- a/core/java/android/app/admin/IntentFilterPolicyKey.java +++ b/core/java/android/app/admin/IntentFilterPolicyKey.java @@ -19,12 +19,12 @@ package android.app.admin; import static android.app.admin.PolicyUpdateReceiver.EXTRA_INTENT_FILTER; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.admin.flags.Flags; import android.content.IntentFilter; import android.os.Bundle; import android.os.Parcel; @@ -60,7 +60,7 @@ public final class IntentFilterPolicyKey extends PolicyKey { @TestApi public IntentFilterPolicyKey(@NonNull String identifier, @NonNull IntentFilter filter) { super(identifier); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxParcelableFieldsLength(filter); } mFilter = Objects.requireNonNull(filter); diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java index 9d6ce243a19b..a36ea0508a95 100644 --- a/core/java/android/app/admin/LockTaskPolicy.java +++ b/core/java/android/app/admin/LockTaskPolicy.java @@ -16,11 +16,10 @@ package android.app.admin; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.admin.flags.Flags; import android.os.Parcel; import android.os.Parcelable; @@ -136,7 +135,7 @@ public final class LockTaskPolicy extends PolicyValue<LockTaskPolicy> { } private void setPackagesInternal(Set<String> packages) { - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { for (String p : packages) { PolicySizeVerifier.enforceMaxPackageNameLength(p); } diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java index 2241fddb7320..389585f036db 100644 --- a/core/java/android/app/admin/PackagePermissionPolicyKey.java +++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java @@ -20,12 +20,12 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_PACKAGE_NAME; import static android.app.admin.PolicyUpdateReceiver.EXTRA_PERMISSION_NAME; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.admin.flags.Flags; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -59,7 +59,7 @@ public final class PackagePermissionPolicyKey extends PolicyKey { public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName, @NonNull String permissionName) { super(identifier); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxPackageNameLength(packageName); PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName"); } diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java index 2ea17a18f6a6..68dc797f6513 100644 --- a/core/java/android/app/admin/PackagePolicyKey.java +++ b/core/java/android/app/admin/PackagePolicyKey.java @@ -19,12 +19,12 @@ package android.app.admin; import static android.app.admin.PolicyUpdateReceiver.EXTRA_PACKAGE_NAME; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.admin.flags.Flags; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -55,7 +55,7 @@ public final class PackagePolicyKey extends PolicyKey { @TestApi public PackagePolicyKey(@NonNull String key, @NonNull String packageName) { super(key); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxPackageNameLength(packageName); } mPackageName = Objects.requireNonNull((packageName)); diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java index f4d4adcfcedb..8995c0f20de8 100644 --- a/core/java/android/app/admin/StringPolicyValue.java +++ b/core/java/android/app/admin/StringPolicyValue.java @@ -16,10 +16,9 @@ package android.app.admin; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.flags.Flags; import android.os.Parcel; import java.util.Objects; @@ -31,7 +30,7 @@ public final class StringPolicyValue extends PolicyValue<String> { public StringPolicyValue(@NonNull String value) { super(value); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxStringLength(value, "policyValue"); } } diff --git a/core/java/android/app/admin/StringSetPolicyValue.java b/core/java/android/app/admin/StringSetPolicyValue.java index 82fe761a414f..f37dfee0f9dc 100644 --- a/core/java/android/app/admin/StringSetPolicyValue.java +++ b/core/java/android/app/admin/StringSetPolicyValue.java @@ -16,10 +16,9 @@ package android.app.admin; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.flags.Flags; import android.os.Parcel; import java.util.HashSet; @@ -33,7 +32,7 @@ public final class StringSetPolicyValue extends PolicyValue<Set<String>> { public StringSetPolicyValue(@NonNull Set<String> value) { super(value); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { for (String str : value) { PolicySizeVerifier.enforceMaxStringLength(str, "policyValue"); } diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java index d69a5f08ce2e..ee90ccd9417f 100644 --- a/core/java/android/app/admin/UserRestrictionPolicyKey.java +++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java @@ -17,11 +17,11 @@ package android.app.admin; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.admin.flags.Flags; import android.os.Bundle; import android.os.Parcel; @@ -45,7 +45,7 @@ public final class UserRestrictionPolicyKey extends PolicyKey { @TestApi public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) { super(identifier); - if (devicePolicySizeTrackingEnabled()) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction"); } mRestriction = Objects.requireNonNull(restriction); diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index e1a69139d88d..19270199696e 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -10,7 +10,14 @@ flag { flag { name: "device_policy_size_tracking_enabled" namespace: "enterprise" - description: "Add feature to track the total policy size and have a max threshold." + description: "Add feature to track the total policy size and have a max threshold - public API changes" + bug: "281543351" +} + +flag { + name: "device_policy_size_tracking_internal_enabled" + namespace: "enterprise" + description: "Add feature to track the total policy size and have a max threshold - internal changes" bug: "281543351" } diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java index 1a8136e06c28..7383d07c82e9 100644 --- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java +++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java @@ -16,16 +16,24 @@ package android.app.servertransaction; +import static com.android.window.flags.Flags.activityWindowInfoFlag; import static com.android.window.flags.Flags.bundleClientTransactionFlag; import static java.util.Objects.requireNonNull; import android.annotation.NonNull; +import android.app.Activity; import android.app.ActivityThread; import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.util.ArraySet; +import android.window.ActivityWindowInfo; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import java.util.function.BiConsumer; + /** * Singleton controller to manage listeners to individual {@link ClientTransaction}. * @@ -35,8 +43,14 @@ public class ClientTransactionListenerController { private static ClientTransactionListenerController sController; + private final Object mLock = new Object(); private final DisplayManagerGlobal mDisplayManager; + /** Listeners registered via {@link #registerActivityWindowInfoChangedListener(BiConsumer)}. */ + @GuardedBy("mLock") + private final ArraySet<BiConsumer<IBinder, ActivityWindowInfo>> + mActivityWindowInfoChangedListeners = new ArraySet<>(); + /** Gets the singleton controller. */ @NonNull public static ClientTransactionListenerController getInstance() { @@ -62,6 +76,57 @@ public class ClientTransactionListenerController { } /** + * Registers to listen on activity {@link ActivityWindowInfo} change. + * The listener will be invoked with two parameters: {@link Activity#getActivityToken()} and + * {@link ActivityWindowInfo}. + */ + public void registerActivityWindowInfoChangedListener( + @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) { + if (!activityWindowInfoFlag()) { + return; + } + synchronized (mLock) { + mActivityWindowInfoChangedListeners.add(listener); + } + } + + /** + * Unregisters the listener that was previously registered via + * {@link #registerActivityWindowInfoChangedListener(BiConsumer)} + */ + public void unregisterActivityWindowInfoChangedListener( + @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) { + if (!activityWindowInfoFlag()) { + return; + } + synchronized (mLock) { + mActivityWindowInfoChangedListeners.remove(listener); + } + } + + /** + * Called when receives a {@link ClientTransaction} that is updating an activity's + * {@link ActivityWindowInfo}. + */ + public void onActivityWindowInfoChanged(@NonNull IBinder activityToken, + @NonNull ActivityWindowInfo activityWindowInfo) { + if (!activityWindowInfoFlag()) { + return; + } + final Object[] activityWindowInfoChangedListeners; + synchronized (mLock) { + if (mActivityWindowInfoChangedListeners.isEmpty()) { + return; + } + activityWindowInfoChangedListeners = mActivityWindowInfoChangedListeners.toArray(); + } + for (Object activityWindowInfoChangedListener : activityWindowInfoChangedListeners) { + ((BiConsumer<IBinder, ActivityWindowInfo>) activityWindowInfoChangedListener) + .accept(activityToken, activityWindowInfo); + } + } + + /** * Called when receives a {@link ClientTransaction} that is updating display-related * window configuration. */ diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index f9cf075d6062..b0213d7356df 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -33,7 +33,6 @@ import android.os.Trace; public class DestroyActivityItem extends ActivityLifecycleItem { private boolean mFinished; - private int mConfigChanges; @Override public void preExecute(@NonNull ClientTransactionHandler client) { @@ -44,7 +43,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem { public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @NonNull PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy"); - client.handleDestroyActivity(r, mFinished, mConfigChanges, + client.handleDestroyActivity(r, mFinished, false /* getNonConfigInstance */, "DestroyActivityItem"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -67,15 +66,13 @@ public class DestroyActivityItem extends ActivityLifecycleItem { /** Obtain an instance initialized with provided params. */ @NonNull - public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished, - int configChanges) { + public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished) { DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class); if (instance == null) { instance = new DestroyActivityItem(); } instance.setActivityToken(activityToken); instance.mFinished = finished; - instance.mConfigChanges = configChanges; return instance; } @@ -84,7 +81,6 @@ public class DestroyActivityItem extends ActivityLifecycleItem { public void recycle() { super.recycle(); mFinished = false; - mConfigChanges = 0; ObjectPool.recycle(this); } @@ -95,14 +91,12 @@ public class DestroyActivityItem extends ActivityLifecycleItem { public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeBoolean(mFinished); - dest.writeInt(mConfigChanges); } /** Read from Parcel. */ private DestroyActivityItem(@NonNull Parcel in) { super(in); mFinished = in.readBoolean(); - mConfigChanges = in.readInt(); } public static final @NonNull Creator<DestroyActivityItem> CREATOR = new Creator<>() { @@ -124,7 +118,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem { return false; } final DestroyActivityItem other = (DestroyActivityItem) o; - return mFinished == other.mFinished && mConfigChanges == other.mConfigChanges; + return mFinished == other.mFinished; } @Override @@ -132,14 +126,12 @@ public class DestroyActivityItem extends ActivityLifecycleItem { int result = 17; result = 31 * result + super.hashCode(); result = 31 * result + (mFinished ? 1 : 0); - result = 31 * result + mConfigChanges; return result; } @Override public String toString() { return "DestroyActivityItem{" + super.toString() - + ",finished=" + mFinished - + ",mConfigChanges=" + mConfigChanges + "}"; + + ",finished=" + mFinished + "}"; } } diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 8f1e90b985e6..d230284287b6 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -37,7 +37,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { private boolean mFinished; private boolean mUserLeaving; - private int mConfigChanges; private boolean mDontReport; private boolean mAutoEnteringPip; @@ -45,7 +44,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @NonNull PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); - client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, mAutoEnteringPip, + client.handlePauseActivity(r, mFinished, mUserLeaving, mAutoEnteringPip, pendingActions, "PAUSE_ACTIVITY_ITEM"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -72,7 +71,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { /** Obtain an instance initialized with provided params. */ @NonNull public static PauseActivityItem obtain(@NonNull IBinder activityToken, boolean finished, - boolean userLeaving, int configChanges, boolean dontReport, boolean autoEnteringPip) { + boolean userLeaving, boolean dontReport, boolean autoEnteringPip) { PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class); if (instance == null) { instance = new PauseActivityItem(); @@ -80,7 +79,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { instance.setActivityToken(activityToken); instance.mFinished = finished; instance.mUserLeaving = userLeaving; - instance.mConfigChanges = configChanges; instance.mDontReport = dontReport; instance.mAutoEnteringPip = autoEnteringPip; @@ -91,7 +89,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { @NonNull public static PauseActivityItem obtain(@NonNull IBinder activityToken) { return obtain(activityToken, false /* finished */, false /* userLeaving */, - 0 /* configChanges */, true /* dontReport */, false /* autoEnteringPip*/); + true /* dontReport */, false /* autoEnteringPip*/); } @Override @@ -99,7 +97,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { super.recycle(); mFinished = false; mUserLeaving = false; - mConfigChanges = 0; mDontReport = false; mAutoEnteringPip = false; ObjectPool.recycle(this); @@ -113,7 +110,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { super.writeToParcel(dest, flags); dest.writeBoolean(mFinished); dest.writeBoolean(mUserLeaving); - dest.writeInt(mConfigChanges); dest.writeBoolean(mDontReport); dest.writeBoolean(mAutoEnteringPip); } @@ -123,7 +119,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { super(in); mFinished = in.readBoolean(); mUserLeaving = in.readBoolean(); - mConfigChanges = in.readInt(); mDontReport = in.readBoolean(); mAutoEnteringPip = in.readBoolean(); } @@ -148,7 +143,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { } final PauseActivityItem other = (PauseActivityItem) o; return mFinished == other.mFinished && mUserLeaving == other.mUserLeaving - && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport + && mDontReport == other.mDontReport && mAutoEnteringPip == other.mAutoEnteringPip; } @@ -158,7 +153,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { result = 31 * result + super.hashCode(); result = 31 * result + (mFinished ? 1 : 0); result = 31 * result + (mUserLeaving ? 1 : 0); - result = 31 * result + mConfigChanges; result = 31 * result + (mDontReport ? 1 : 0); result = 31 * result + (mAutoEnteringPip ? 1 : 0); return result; @@ -169,7 +163,6 @@ public class PauseActivityItem extends ActivityLifecycleItem { return "PauseActivityItem{" + super.toString() + ",finished=" + mFinished + ",userLeaving=" + mUserLeaving - + ",configChanges=" + mConfigChanges + ",dontReport=" + mDontReport + ",autoEnteringPip=" + mAutoEnteringPip + "}"; } diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index b8ce52da5a0c..def7b3fd9987 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -19,7 +19,6 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; @@ -34,13 +33,11 @@ public class StopActivityItem extends ActivityLifecycleItem { private static final String TAG = "StopActivityItem"; - private int mConfigChanges; - @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @NonNull PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop"); - client.handleStopActivity(r, mConfigChanges, pendingActions, + client.handleStopActivity(r, pendingActions, true /* finalStateRequest */, "STOP_ACTIVITY_ITEM"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -63,16 +60,14 @@ public class StopActivityItem extends ActivityLifecycleItem { /** * Obtain an instance initialized with provided params. * @param activityToken the activity that stops. - * @param configChanges Configuration pieces that changed. */ @NonNull - public static StopActivityItem obtain(@NonNull IBinder activityToken, int configChanges) { + public static StopActivityItem obtain(@NonNull IBinder activityToken) { StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class); if (instance == null) { instance = new StopActivityItem(); } instance.setActivityToken(activityToken); - instance.mConfigChanges = configChanges; return instance; } @@ -80,23 +75,14 @@ public class StopActivityItem extends ActivityLifecycleItem { @Override public void recycle() { super.recycle(); - mConfigChanges = 0; ObjectPool.recycle(this); } // Parcelable implementation - /** Write to Parcel. */ - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(mConfigChanges); - } - /** Read from Parcel. */ private StopActivityItem(@NonNull Parcel in) { super(in); - mConfigChanges = in.readInt(); } public static final @NonNull Creator<StopActivityItem> CREATOR = new Creator<>() { @@ -110,28 +96,7 @@ public class StopActivityItem extends ActivityLifecycleItem { }; @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!super.equals(o)) { - return false; - } - final StopActivityItem other = (StopActivityItem) o; - return mConfigChanges == other.mConfigChanges; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + super.hashCode(); - result = 31 * result + mConfigChanges; - return result; - } - - @Override public String toString() { - return "StopActivityItem{" + super.toString() - + ",configChanges=" + mConfigChanges + "}"; + return "StopActivityItem{" + super.toString() + "}"; } } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index fa73c99be2b8..c83719149821 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -334,18 +334,18 @@ public class TransactionExecutor { break; case ON_PAUSE: mTransactionHandler.handlePauseActivity(r, false /* finished */, - false /* userLeaving */, 0 /* configChanges */, + false /* userLeaving */, false /* autoEnteringPip */, mPendingActions, "LIFECYCLER_PAUSE_ACTIVITY"); break; case ON_STOP: - mTransactionHandler.handleStopActivity(r, 0 /* configChanges */, + mTransactionHandler.handleStopActivity(r, mPendingActions, false /* finalStateRequest */, "LIFECYCLER_STOP_ACTIVITY"); break; case ON_DESTROY: mTransactionHandler.handleDestroyActivity(r, false /* finishing */, - 0 /* configChanges */, false /* getNonConfigInstance */, + false /* getNonConfigInstance */, "performLifecycleSequence. cycling to:" + path.get(size - 1)); break; case ON_RESTART: diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 475c6fb9a48a..710261ab4c97 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -200,7 +200,7 @@ public class TransactionExecutorHelper { lifecycleItem = PauseActivityItem.obtain(r.token); break; case ON_STOP: - lifecycleItem = StopActivityItem.obtain(r.token, 0 /* configChanges */); + lifecycleItem = StopActivityItem.obtain(r.token); break; default: lifecycleItem = ResumeActivityItem.obtain(r.token, false /* isForward */, diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index 39f6de75928a..00d534370fa1 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -16,6 +16,9 @@ package android.companion.virtual; +import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; +import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO; + import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; @@ -352,12 +355,20 @@ public class VirtualDeviceInternal { @Nullable Executor executor, @Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) { if (mVirtualAudioDevice == null) { - Context context = mContext; - if (Flags.deviceAwareRecordAudioPermission()) { - context = mContext.createDeviceContext(getDeviceId()); + try { + Context context = mContext; + if (Flags.deviceAwareRecordAudioPermission()) { + // When using a default policy for audio device-aware RECORD_AUDIO permission + // should not take effect, thus register policies with the default context. + if (mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM) { + context = mContext.createDeviceContext(getDeviceId()); + } + } + mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display, + executor, callback, () -> mVirtualAudioDevice = null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display, - executor, callback, () -> mVirtualAudioDevice = null); } return mVirtualAudioDevice; } diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 3e9f260566bd..8a3a3ad56a7b 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -112,6 +112,12 @@ public class UserInfo implements Parcelable { /** * Indicates that this user is disabled. * + * <p> This is currently used to indicate that a Managed Profile, when created via + * DevicePolicyManager, has not yet been provisioned; once the DPC provisions it, a DPM call + * will manually set it to enabled. + * + * <p>Users that are slated for deletion are also generally set to disabled. + * * <p>Note: If an ephemeral user is disabled, it shouldn't be later re-enabled. Ephemeral users * are disabled as their removal is in progress to indicate that they shouldn't be re-entered. */ @@ -398,6 +404,7 @@ public class UserInfo implements Parcelable { return UserManager.isUserTypePrivateProfile(userType); } + /** See {@link #FLAG_DISABLED}*/ @UnsupportedAppUsage public boolean isEnabled() { return (flags & FLAG_DISABLED) != FLAG_DISABLED; diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 5e9d8f0a9b7e..610057bffdbf 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -208,6 +208,14 @@ flag { } flag { + name: "restrict_nonpreloads_system_shareduids" + namespace: "package_manager_service" + description: "Feature flag to restrict apps from joining system shared uids" + bug: "308573169" + is_fixed_read_only: true +} + +flag { name: "min_target_sdk_24" namespace: "responsible_apis" description: "Feature flag to bump min target sdk to 24" diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index d259e9755a41..273e40a21bb2 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -471,6 +471,16 @@ public final class AssetManager implements AutoCloseable { return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); } + /** + * @hide + */ + public void addSharedLibraryPaths(@NonNull String[] paths) { + final int length = paths.length; + for (int i = 0; i < length; i++) { + addAssetPathInternal(paths[i], false, true); + } + } + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { Objects.requireNonNull(path, "path"); synchronized (this) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 7fba3e890ec6..1f5f88f51d55 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -43,6 +43,7 @@ import android.annotation.StyleRes; import android.annotation.StyleableRes; import android.annotation.XmlRes; import android.app.Application; +import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ActivityInfo; @@ -2854,6 +2855,11 @@ public class Resources { @FlaggedApi(android.content.res.Flags.FLAG_REGISTER_RESOURCE_PATHS) public static void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) { - throw new UnsupportedOperationException("The implementation has not been done yet."); + if (Flags.registerResourcePaths()) { + ResourcesManager.getInstance().registerResourcePaths(uniqueId, appInfo); + } else { + throw new UnsupportedOperationException("Flag " + Flags.FLAG_REGISTER_RESOURCE_PATHS + + " is disabled."); + } } } diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 079c2c1ab7c9..8d045aaf4d81 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -29,6 +29,7 @@ import android.annotation.StyleRes; import android.annotation.StyleableRes; import android.app.LocaleConfig; import android.app.ResourcesManager; +import android.app.ResourcesManager.SharedLibraryAssets; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.Config; @@ -47,6 +48,7 @@ import android.os.Build; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.os.Trace; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -197,6 +199,14 @@ public class ResourcesImpl { public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics, @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) { mAssets = assets; + if (Flags.registerResourcePaths()) { + ArrayMap<String, SharedLibraryAssets> sharedLibMap = + ResourcesManager.getInstance().getSharedLibAssetsMap(); + final int size = sharedLibMap.size(); + for (int i = 0; i < size; i++) { + assets.addSharedLibraryPaths(sharedLibMap.valueAt(i).getAllAssetPaths()); + } + } mMetrics.setToDefaults(); mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index 2e63664df7aa..3a9a0f911660 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -23,6 +23,7 @@ import android.annotation.Hide; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.TestApi; @@ -31,6 +32,7 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.IntentSender; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; @@ -57,6 +59,7 @@ import java.util.concurrent.Executor; * to authenticate to the app. */ @SystemService(Context.CREDENTIAL_SERVICE) +@RequiresFeature(PackageManager.FEATURE_CREDENTIALS) public final class CredentialManager { private static final String TAG = "CredentialManager"; private static final Bundle OPTIONS_SENDER_BAL_OPTIN = ActivityOptions.makeBasic() diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java index 4b0fa6d32af7..79fba9b19250 100644 --- a/core/java/android/credentials/selection/IntentFactory.java +++ b/core/java/android/credentials/selection/IntentFactory.java @@ -80,17 +80,7 @@ public class IntentFactory { ArrayList<DisabledProviderData> disabledProviderDataList, @NonNull ResultReceiver resultReceiver) { Intent intent = new Intent(); - ComponentName componentName = - ComponentName.unflattenFromString( - Resources.getSystem() - .getString( - com.android.internal.R.string - .config_credentialManagerDialogComponent)); - ComponentName oemOverrideComponentName = getOemOverrideComponentName(context); - if (oemOverrideComponentName != null) { - componentName = oemOverrideComponentName; - } - intent.setComponent(componentName); + setCredentialSelectorUiComponentName(context, intent); intent.putParcelableArrayListExtra( ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList); intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo); @@ -100,6 +90,24 @@ public class IntentFactory { return intent; } + private static void setCredentialSelectorUiComponentName(@NonNull Context context, + @NonNull Intent intent) { + if (configurableSelectorUiEnabled()) { + ComponentName componentName = getOemOverrideComponentName(context); + if (componentName == null) { + componentName = ComponentName.unflattenFromString(Resources.getSystem().getString( + com.android.internal.R.string + .config_fallbackCredentialManagerDialogComponent)); + } + intent.setComponent(componentName); + } else { + ComponentName componentName = ComponentName.unflattenFromString(Resources.getSystem() + .getString(com.android.internal.R.string + .config_fallbackCredentialManagerDialogComponent)); + intent.setComponent(componentName); + } + } + /** * Returns null if there is not an enabled and valid oem override component. It means the * default platform UI component name should be used instead. @@ -107,44 +115,39 @@ public class IntentFactory { @Nullable private static ComponentName getOemOverrideComponentName(@NonNull Context context) { ComponentName result = null; - if (configurableSelectorUiEnabled()) { - if (Resources.getSystem().getBoolean( - com.android.internal.R.bool.config_enableOemCredentialManagerDialogComponent)) { - String oemComponentString = - Resources.getSystem() - .getString( - com.android.internal.R.string - .config_oemCredentialManagerDialogComponent); - if (!TextUtils.isEmpty(oemComponentString)) { - ComponentName oemComponentName = ComponentName.unflattenFromString( - oemComponentString); - if (oemComponentName != null) { - try { - ActivityInfo info = context.getPackageManager().getActivityInfo( - oemComponentName, - PackageManager.ComponentInfoFlags.of( - PackageManager.MATCH_SYSTEM_ONLY)); - if (info.enabled && info.exported) { - Slog.i(TAG, - "Found enabled oem CredMan UI component." - + oemComponentString); - result = oemComponentName; - } else { - Slog.i(TAG, - "Found enabled oem CredMan UI component but it was not " - + "enabled."); - } - } catch (PackageManager.NameNotFoundException e) { - Slog.i(TAG, "Unable to find oem CredMan UI component: " - + oemComponentString + "."); - } + String oemComponentString = + Resources.getSystem() + .getString( + com.android.internal.R.string + .config_oemCredentialManagerDialogComponent); + if (!TextUtils.isEmpty(oemComponentString)) { + ComponentName oemComponentName = ComponentName.unflattenFromString( + oemComponentString); + if (oemComponentName != null) { + try { + ActivityInfo info = context.getPackageManager().getActivityInfo( + oemComponentName, + PackageManager.ComponentInfoFlags.of( + PackageManager.MATCH_SYSTEM_ONLY)); + if (info.enabled && info.exported) { + Slog.i(TAG, + "Found enabled oem CredMan UI component." + + oemComponentString); + result = oemComponentName; } else { - Slog.i(TAG, "Invalid OEM ComponentName format."); + Slog.i(TAG, + "Found enabled oem CredMan UI component but it was not " + + "enabled."); } - } else { - Slog.i(TAG, "Invalid empty OEM component name."); + } catch (PackageManager.NameNotFoundException e) { + Slog.i(TAG, "Unable to find oem CredMan UI component: " + + oemComponentString + "."); } + } else { + Slog.i(TAG, "Invalid OEM ComponentName format."); } + } else { + Slog.i(TAG, "Invalid empty OEM component name."); } return result; } @@ -186,16 +189,11 @@ public class IntentFactory { * Creates an Intent that cancels any UI matching the given request token id. */ @NonNull - public static Intent createCancelUiIntent(@NonNull IBinder requestToken, - boolean shouldShowCancellationUi, @NonNull String appPackageName) { + public static Intent createCancelUiIntent(@NonNull Context context, + @NonNull IBinder requestToken, boolean shouldShowCancellationUi, + @NonNull String appPackageName) { Intent intent = new Intent(); - ComponentName componentName = - ComponentName.unflattenFromString( - Resources.getSystem() - .getString( - com.android.internal.R.string - .config_credentialManagerDialogComponent)); - intent.setComponent(componentName); + setCredentialSelectorUiComponentName(context, intent); intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST, new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi, appPackageName)); diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index d9d4305cc630..0208fed6040f 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -23,7 +23,6 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT; -import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION; import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT; import static android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE; @@ -1128,8 +1127,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @hide */ @Override - @TestApi - @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION) public void onAuthenticationAcquired(int acquireInfo) {} /** diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java index 16f71414c8c9..3b9cad49250f 100644 --- a/core/java/android/hardware/biometrics/SensorProperties.java +++ b/core/java/android/hardware/biometrics/SensorProperties.java @@ -16,9 +16,6 @@ package android.hardware.biometrics; -import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION; - -import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; @@ -144,10 +141,8 @@ public class SensorProperties { /** * @hide */ - @TestApi - @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION) public SensorProperties(int sensorId, @Strength int sensorStrength, - @NonNull List<ComponentInfo> componentInfo) { + List<ComponentInfo> componentInfo) { mSensorId = sensorId; mSensorStrength = sensorStrength; mComponentInfo = componentInfo; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 57b437f4bacf..dc8f4b448931 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -3521,7 +3521,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed * to be supported by the camera HAL in the secure camera mode. Any other format or * resolutions might not be supported. Use - * {@link CameraManager#isSessionConfigurationWithParametersSupported } + * {@link CameraDevice#isSessionConfigurationSupported } * API to query if a secure session configuration is supported if the device supports this * API.</p> * <p>If this key returns null on a device with SECURE_IMAGE_DATA capability, the application @@ -5046,18 +5046,18 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>The version of the session configuration query - * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported } + * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported } * API</p> * <p>The possible values in this key correspond to the values defined in * android.os.Build.VERSION_CODES. Each version defines a set of feature combinations the * camera device must reliably report whether they are supported via - * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported } + * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported } * API. And the version is always less or equal to android.os.Build.VERSION.SDK_INT.</p> * <p>If set to UPSIDE_DOWN_CAKE, this camera device doesn't support - * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }. + * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }. * Calling the method for this camera ID throws an UnsupportedOperationException.</p> * <p>If set to VANILLA_ICE_CREAM, the application can call - * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported } + * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported } * to check if the combinations of below features are supported.</p> * <ul> * <li>A subset of LIMITED-level device stream combinations.</li> @@ -6082,11 +6082,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Minimum and maximum padding zoom factors supported by this camera device for - * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for the + * android.efv.paddingZoomFactor used for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } * extension.</p> * <p>The minimum and maximum padding zoom factors supported by the device for - * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the + * android.efv.paddingZoomFactor used as part of the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } * extension feature. This extension specific camera characteristic can be queried using * {@link android.hardware.camera2.CameraExtensionCharacteristics#get }.</p> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index e24c98e98c5d..9fb561bb4211 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -911,10 +911,10 @@ public abstract class CameraMetadata<TKey> { * </ul> * <p>Combinations of logical and physical streams, or physical streams from different * physical cameras are not guaranteed. However, if the camera device supports - * {@link CameraManager#isSessionConfigurationWithParametersSupported }, + * {@link CameraDevice#isSessionConfigurationSupported }, * application must be able to query whether a stream combination involving physical * streams is supported by calling - * {@link CameraManager#isSessionConfigurationWithParametersSupported }.</p> + * {@link CameraDevice#isSessionConfigurationSupported }.</p> * <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front * camera in the system. For an application that switches between front and back cameras, * the recommendation is to switch between the first rear camera and the first front diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 9fbe348f1e9a..f3b7b919d87d 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -260,7 +260,7 @@ public final class MandatoryStreamCombination { * smaller sizes, then the resulting * {@link android.hardware.camera2.params.SessionConfiguration session configuration} can * be tested either by calling {@link CameraDevice#createCaptureSession} or - * {@link CameraManager#isSessionConfigurationWithParametersSupported}. + * {@link CameraDeviceSetup#isSessionConfigurationSupported}. * * @return non-modifiable ascending list of available sizes. */ diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 1b0a485bdbda..066c45f03ec4 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -18,7 +18,6 @@ package android.hardware.face; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.MANAGE_BIOMETRIC; -import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE; @@ -30,7 +29,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; @@ -38,7 +36,6 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricStateListener; -import android.hardware.biometrics.BiometricTestSession; import android.hardware.biometrics.CryptoObject; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; @@ -72,7 +69,6 @@ import java.util.concurrent.Executor; */ @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION) @SystemApi -@TestApi @SystemService(Context.FACE_SERVICE) public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants { @@ -784,8 +780,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ @NonNull - @TestApi - @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION) public List<FaceSensorProperties> getSensorProperties() { final List<FaceSensorProperties> properties = new ArrayList<>(); final List<FaceSensorPropertiesInternal> internalProperties @@ -1634,23 +1628,4 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan Slog.w(TAG, "Unknown enrollment acquired message: " + acquireInfo + ", " + vendorCode); return null; } - - /** - * Retrieves a test session for FaceManager. - * - * @hide - */ - @TestApi - @NonNull - @RequiresPermission(TEST_BIOMETRIC) - @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION) - public BiometricTestSession createTestSession(int sensorId) { - try { - return new BiometricTestSession(mContext, sensorId, - (context, sensorId1, callback) -> mService - .createTestSession(sensorId1, callback, context.getOpPackageName())); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } -}
\ No newline at end of file +} diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java index a1ddb0e3495b..f61312785919 100644 --- a/core/java/android/hardware/face/FaceSensorProperties.java +++ b/core/java/android/hardware/face/FaceSensorProperties.java @@ -16,12 +16,8 @@ package android.hardware.face; -import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION; - -import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.TestApi; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; @@ -34,8 +30,6 @@ import java.util.List; * Container for face sensor properties. * @hide */ -@TestApi -@FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION) public class FaceSensorProperties extends SensorProperties { /** * @hide diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 6515312e337c..b98c0cb41ac9 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -39,7 +39,7 @@ import android.view.Surface; interface IFaceService { // Creates a test session with the specified sensorId - @EnforcePermission("TEST_BIOMETRIC") + @EnforcePermission("USE_BIOMETRIC_INTERNAL") ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName); // Requests a proto dump of the specified sensor diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 1f5495999416..2816f777e8ab 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -27,6 +27,7 @@ import android.hardware.input.IKeyboardBacklightListener; import android.hardware.input.IKeyboardBacklightState; import android.hardware.input.IStickyModifierStateListener; import android.hardware.input.ITabletModeChangedListener; +import android.hardware.input.KeyboardLayoutSelectionResult; import android.hardware.input.TouchCalibration; import android.os.CombinedVibration; import android.hardware.input.IInputSensorEventListener; @@ -120,8 +121,9 @@ interface IInputManager { String keyboardLayoutDescriptor); // New Keyboard layout config APIs - String getKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, int userId, - in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype); + KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice( + in InputDeviceIdentifier identifier, int userId, in InputMethodInfo imeInfo, + in InputMethodSubtype imeSubtype); @EnforcePermission("SET_KEYBOARD_LAYOUT") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 744dfae97108..a1242fb43bbd 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -784,10 +784,10 @@ public final class InputManager { * * @hide */ - @Nullable - public String getKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier, - @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, - @Nullable InputMethodSubtype imeSubtype) { + @NonNull + public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice( + @NonNull InputDeviceIdentifier identifier, @UserIdInt int userId, + @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) { try { return mIm.getKeyboardLayoutForInputDevice(identifier, userId, imeInfo, imeSubtype); } catch (RemoteException ex) { diff --git a/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl new file mode 100644 index 000000000000..13be2ff5ffb7 --- /dev/null +++ b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +parcelable KeyboardLayoutSelectionResult; diff --git a/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java new file mode 100644 index 000000000000..5a1c9478f629 --- /dev/null +++ b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java @@ -0,0 +1,260 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Provides information about the selected layout and the selection criteria when the caller calls + * {@link InputManager#getKeyboardLayoutForInputDevice(InputDeviceIdentifier, int, InputMethodInfo, + * InputMethodSubtype)} + * + * @hide + */ + +@DataClass(genParcelable = true, genToString = true, genEqualsHashCode = true) +public final class KeyboardLayoutSelectionResult implements Parcelable { + @Nullable + private final String mLayoutDescriptor; + + /** Unspecified layout selection criteria */ + public static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED = 0; + + /** Manual selection by user */ + public static final int LAYOUT_SELECTION_CRITERIA_USER = 1; + + /** Auto-detection based on device provided language tag and layout type */ + public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 2; + + /** Auto-detection based on IME provided language tag and layout type */ + public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 3; + + /** Default selection */ + public static final int LAYOUT_SELECTION_CRITERIA_DEFAULT = 4; + + /** Failed layout selection */ + public static final KeyboardLayoutSelectionResult FAILED = new KeyboardLayoutSelectionResult( + null, LAYOUT_SELECTION_CRITERIA_UNSPECIFIED); + + @LayoutSelectionCriteria + private final int mSelectionCriteria; + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @IntDef(prefix = "LAYOUT_SELECTION_CRITERIA_", value = { + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED, + LAYOUT_SELECTION_CRITERIA_USER, + LAYOUT_SELECTION_CRITERIA_DEVICE, + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, + LAYOUT_SELECTION_CRITERIA_DEFAULT + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface LayoutSelectionCriteria {} + + @DataClass.Generated.Member + public static String layoutSelectionCriteriaToString(@LayoutSelectionCriteria int value) { + switch (value) { + case LAYOUT_SELECTION_CRITERIA_UNSPECIFIED: + return "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED"; + case LAYOUT_SELECTION_CRITERIA_USER: + return "LAYOUT_SELECTION_CRITERIA_USER"; + case LAYOUT_SELECTION_CRITERIA_DEVICE: + return "LAYOUT_SELECTION_CRITERIA_DEVICE"; + case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD: + return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD"; + case LAYOUT_SELECTION_CRITERIA_DEFAULT: + return "LAYOUT_SELECTION_CRITERIA_DEFAULT"; + default: return Integer.toHexString(value); + } + } + + @DataClass.Generated.Member + public KeyboardLayoutSelectionResult( + @Nullable String layoutDescriptor, + @LayoutSelectionCriteria int selectionCriteria) { + this.mLayoutDescriptor = layoutDescriptor; + this.mSelectionCriteria = selectionCriteria; + + if (!(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_UNSPECIFIED) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT)) { + throw new java.lang.IllegalArgumentException( + "selectionCriteria was " + mSelectionCriteria + " but must be one of: " + + "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED(" + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED + "), " + + "LAYOUT_SELECTION_CRITERIA_USER(" + LAYOUT_SELECTION_CRITERIA_USER + "), " + + "LAYOUT_SELECTION_CRITERIA_DEVICE(" + LAYOUT_SELECTION_CRITERIA_DEVICE + "), " + + "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD(" + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + "), " + + "LAYOUT_SELECTION_CRITERIA_DEFAULT(" + LAYOUT_SELECTION_CRITERIA_DEFAULT + ")"); + } + + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @Nullable String getLayoutDescriptor() { + return mLayoutDescriptor; + } + + @DataClass.Generated.Member + public @LayoutSelectionCriteria int getSelectionCriteria() { + return mSelectionCriteria; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "KeyboardLayoutSelectionResult { " + + "layoutDescriptor = " + mLayoutDescriptor + ", " + + "selectionCriteria = " + layoutSelectionCriteriaToString(mSelectionCriteria) + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(KeyboardLayoutSelectionResult other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + KeyboardLayoutSelectionResult that = (KeyboardLayoutSelectionResult) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mLayoutDescriptor, that.mLayoutDescriptor) + && mSelectionCriteria == that.mSelectionCriteria; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mLayoutDescriptor); + _hash = 31 * _hash + mSelectionCriteria; + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mLayoutDescriptor != null) flg |= 0x1; + dest.writeByte(flg); + if (mLayoutDescriptor != null) dest.writeString(mLayoutDescriptor); + dest.writeInt(mSelectionCriteria); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ KeyboardLayoutSelectionResult(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String layoutDescriptor = (flg & 0x1) == 0 ? null : in.readString(); + int selectionCriteria = in.readInt(); + + this.mLayoutDescriptor = layoutDescriptor; + this.mSelectionCriteria = selectionCriteria; + + if (!(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_UNSPECIFIED) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD) + && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT)) { + throw new java.lang.IllegalArgumentException( + "selectionCriteria was " + mSelectionCriteria + " but must be one of: " + + "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED(" + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED + "), " + + "LAYOUT_SELECTION_CRITERIA_USER(" + LAYOUT_SELECTION_CRITERIA_USER + "), " + + "LAYOUT_SELECTION_CRITERIA_DEVICE(" + LAYOUT_SELECTION_CRITERIA_DEVICE + "), " + + "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD(" + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + "), " + + "LAYOUT_SELECTION_CRITERIA_DEFAULT(" + LAYOUT_SELECTION_CRITERIA_DEFAULT + ")"); + } + + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<KeyboardLayoutSelectionResult> CREATOR + = new Parcelable.Creator<KeyboardLayoutSelectionResult>() { + @Override + public KeyboardLayoutSelectionResult[] newArray(int size) { + return new KeyboardLayoutSelectionResult[size]; + } + + @Override + public KeyboardLayoutSelectionResult createFromParcel(@NonNull android.os.Parcel in) { + return new KeyboardLayoutSelectionResult(in); + } + }; + + @DataClass.Generated( + time = 1709568115865L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java", + inputSignatures = "private final @android.annotation.Nullable java.lang.String mLayoutDescriptor\npublic static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED\npublic static final int LAYOUT_SELECTION_CRITERIA_USER\npublic static final int LAYOUT_SELECTION_CRITERIA_DEVICE\npublic static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD\npublic static final int LAYOUT_SELECTION_CRITERIA_DEFAULT\npublic static final android.hardware.input.KeyboardLayoutSelectionResult FAILED\nprivate final @android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria int mSelectionCriteria\nclass KeyboardLayoutSelectionResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 05a1abeaf479..b41753413baf 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -240,7 +240,7 @@ public abstract class BatteryConsumer { new Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_ANY); /** - * Identifies power attribution dimensions that are captured by an data element of + * Identifies power attribution dimensions that are captured by a data element of * a BatteryConsumer. These Keys are used to access those values and to set them using * Builders. See for example {@link #getConsumedPower(Key)}. * diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e09094203ad4..c611cb972b2c 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1587,7 +1587,7 @@ public abstract class BatteryStats { outNumOfInterest[0] = numOfInterest; } - // The estimated time is the average time we spend in each level, multipled + // The estimated time is the average time we spend in each level, multiplied // by 100 -- the total number of battery levels return (total / numOfInterest) * 100; } @@ -2019,7 +2019,7 @@ public abstract class BatteryStats { public static final int EVENT_TOP = 0x0003; // Event is about active sync operations. public static final int EVENT_SYNC = 0x0004; - // Events for all additional wake locks aquired/release within a wake block. + // Events for all additional wake locks acquired/release within a wake block. // These are not generated by default. public static final int EVENT_WAKE_LOCK = 0x0005; // Event is about an application executing a scheduled job. @@ -3419,7 +3419,7 @@ public abstract class BatteryStats { * incoming service calls from apps. The result is returned as an array of longs, * organized as a sequence like this: * <pre> - * cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ... + * cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ... * </pre> * * @see com.android.internal.os.CpuScalingPolicies#getPolicies diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 3149de4c39e7..beb9a935a6ee 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -120,7 +120,6 @@ public class GraphicsEnvironment { private ClassLoader mClassLoader; private String mLibrarySearchPaths; private String mLibraryPermittedPaths; - private GameManager mGameManager; private int mAngleOptInIndex = -1; private boolean mShouldUseAngle = false; @@ -134,8 +133,6 @@ public class GraphicsEnvironment { final ApplicationInfo appInfoWithMetaData = getAppInfoWithMetadata(context, pm, packageName); - mGameManager = context.getSystemService(GameManager.class); - Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers"); setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData); Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); @@ -161,9 +158,11 @@ public class GraphicsEnvironment { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "notifyGraphicsEnvironmentSetup"); - if (mGameManager != null - && appInfoWithMetaData.category == ApplicationInfo.CATEGORY_GAME) { - mGameManager.notifyGraphicsEnvironmentSetup(); + if (appInfoWithMetaData.category == ApplicationInfo.CATEGORY_GAME) { + final GameManager gameManager = context.getSystemService(GameManager.class); + if (gameManager != null) { + gameManager.notifyGraphicsEnvironmentSetup(); + } } Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index 9fd37d4548ac..fb500a96c737 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -618,7 +618,7 @@ public class HwParcel { */ @FastNative @NonNull - public native final @Nullable + public native final HidlMemory readEmbeddedHidlMemory(long fieldHandle, long parentHandle, long offset); /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index ccb534eb1019..9757a1096a30 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4708,6 +4708,9 @@ public class UserManager { * Sets the user as enabled, if such an user exists. * * <p>Note that the default is true, it's only that managed profiles might not be enabled. + * (Managed profiles created by DevicePolicyManager will start out disabled, and DPM will later + * toggle them to enabled once they are provisioned. This is the primary purpose of the + * {@link UserInfo#FLAG_DISABLED} flag.) * Also ephemeral users can be disabled to indicate that their removal is in progress and they * shouldn't be re-entered. Therefore ephemeral users should not be re-enabled once disabled. * @@ -5259,7 +5262,7 @@ public class UserManager { /** * Returns list of the profiles of userId including userId itself. - * Note that this returns only enabled. + * Note that this returns only {@link UserInfo#isEnabled() enabled} profiles. * <p>Note that this includes all profile types (not including Restricted profiles). * * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index abfa4e3dd8dc..d9400accb4bd 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -1,4 +1,5 @@ package: "android.os" +container: "system" flag { name: "android_os_build_vanilla_ice_cream" @@ -40,6 +41,7 @@ flag { namespace: "profile_experiences" description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion." bug: "299069460" + is_exported: true } flag { diff --git a/core/java/android/os/health/HealthStatsWriter.java b/core/java/android/os/health/HealthStatsWriter.java index d4d10b056c5c..4118775af28a 100644 --- a/core/java/android/os/health/HealthStatsWriter.java +++ b/core/java/android/os/health/HealthStatsWriter.java @@ -58,7 +58,7 @@ public class HealthStatsWriter { * Construct a HealthStatsWriter object with the given constants. * * The "getDataType()" of the resulting HealthStats object will be the - * short name of the java class that the Constants object was initalized + * short name of the java class that the Constants object was initialized * with. */ public HealthStatsWriter(HealthKeys.Constants constants) { diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 88096ab91261..ada708bda34c 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -52,7 +52,7 @@ import java.util.concurrent.Executor; * * After the installation is completed, the device will be running in the new system on next the * reboot. Then, when the user reboots the device again, it will leave {@code DynamicSystem} and go - * back to the original system. While running in {@code DynamicSystem}, persitent storage for + * back to the original system. While running in {@code DynamicSystem}, persistent storage for * factory reset protection (FRP) remains unchanged. Since the user is running the new system with * a temporarily created data partition, their original user data are kept unchanged.</p> * diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java index 536795bafb1c..8ce87e97da9c 100644 --- a/core/java/android/os/image/DynamicSystemManager.java +++ b/core/java/android/os/image/DynamicSystemManager.java @@ -172,7 +172,7 @@ public class DynamicSystemManager { } } /** - * Finish a previously started installation. Installations without a cooresponding + * Finish a previously started installation. Installations without a corresponding * finishInstallation() will be cleaned up during device boot. */ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 5a095410bef6..d45a17f7194e 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1395,7 +1395,7 @@ public class StorageManager { // Package name can be null if the activity thread is running but the app // hasn't bound yet. In this case we fall back to the first package in the // current UID. This works for runtime permissions as permission state is - // per UID and permission realted app ops are updated for all UID packages. + // per UID and permission related app ops are updated for all UID packages. String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( android.os.Process.myUid()); if (packageNames == null || packageNames.length <= 0) { diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index e1f112af41e7..4cf2fd4dce69 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -62,7 +62,7 @@ import java.util.UUID; * <li>To get access to standard directories (like the {@link Environment#DIRECTORY_PICTURES}), they * can use the {@link #createAccessIntent(String)}. This is the recommend way, since it provides a * simpler API and narrows the access to the given directory (and its descendants). - * <li>To get access to any directory (and its descendants), they can use the Storage Acess + * <li>To get access to any directory (and its descendants), they can use the Storage Access * Framework APIs (such as {@link Intent#ACTION_OPEN_DOCUMENT} and * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, although these APIs do not guarantee the user will * select this specific volume. diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index fa9f03da6372..410f51045b9d 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -1788,6 +1788,9 @@ public final class PermissionManager { /** * Gets the permission states for requested package and persistent device. + * <p> + * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the + * exact permission states for the requested device. * * @param packageName name of the package you are checking against * @param persistentDeviceId id of the persistent device you are checking against @@ -2073,5 +2076,29 @@ public final class PermissionManager { return new PermissionState[size]; } }; + + /** @hide */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PermissionState that = (PermissionState) o; + return mGranted == that.mGranted && mFlags == that.mFlags; + } + + /** @hide */ + @Override + public int hashCode() { + return Objects.hash(mGranted, mFlags); + } + + /** @hide */ + @Override + public String toString() { + return "PermissionState{" + + "mGranted=" + mGranted + + ", mFlags=" + mFlags + + '}'; + } } } diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index de7008b19f54..dc782d4e1e9b 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -138,3 +138,11 @@ flag { bug: "325356776" } +flag { + name: "runtime_permission_appops_mapping_enabled" + is_fixed_read_only: true + namespace: "permissions" + description: "Use runtime permission state to determine appop state" + bug: "266164193" +} + diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index c03dc718d1a1..120846ca593b 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -667,7 +667,8 @@ public class CallLog { @FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER) public @NonNull AddCallParametersBuilder setAssertedDisplayName( String assertedDisplayName) { - if (assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) { + if (assertedDisplayName != null + && assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) { throw new IllegalArgumentException("assertedDisplayName exceeds the character" + " limit of " + MAX_NUMBER_OF_CHARACTERS + "."); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 51585af10f5d..eea6464c9047 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11576,6 +11576,15 @@ public final class Settings { "extra_low_power_warning_acknowledged"; /** + * Whether the emergency thermal alert would be disabled + * (0: default) or not (1). + * + * @hide + */ + public static final String EMERGENCY_THERMAL_ALERT_DISABLED = + "emergency_thermal_alert_disabled"; + + /** * 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been * suppressed. * @hide diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/core/java/android/service/notification/ZenAdapters.java index 37b263c3e3bd..b249815ce8db 100644 --- a/services/core/java/com/android/server/notification/ZenAdapters.java +++ b/core/java/android/service/notification/ZenAdapters.java @@ -14,25 +14,28 @@ * limitations under the License. */ -package com.android.server.notification; +package android.service.notification; +import android.annotation.NonNull; import android.app.Flags; import android.app.NotificationManager.Policy; -import android.service.notification.ZenModeConfig; -import android.service.notification.ZenPolicy; /** * Converters between different Zen representations. + * @hide */ -class ZenAdapters { +public class ZenAdapters { - static ZenPolicy notificationPolicyToZenPolicy(Policy policy) { + /** Maps {@link Policy} to {@link ZenPolicy}. */ + @NonNull + public static ZenPolicy notificationPolicyToZenPolicy(@NonNull Policy policy) { ZenPolicy.Builder zenPolicyBuilder = new ZenPolicy.Builder() .allowAlarms(policy.allowAlarms()) .allowCalls( policy.allowCalls() - ? ZenModeConfig.getZenPolicySenders(policy.allowCallsFrom()) - : ZenPolicy.PEOPLE_TYPE_NONE) + ? notificationPolicySendersToZenPolicyPeopleType( + policy.allowCallsFrom()) + : ZenPolicy.PEOPLE_TYPE_NONE) .allowConversations( policy.allowConversations() ? notificationPolicyConversationSendersToZenPolicy( @@ -42,7 +45,8 @@ class ZenAdapters { .allowMedia(policy.allowMedia()) .allowMessages( policy.allowMessages() - ? ZenModeConfig.getZenPolicySenders(policy.allowMessagesFrom()) + ? notificationPolicySendersToZenPolicyPeopleType( + policy.allowMessagesFrom()) : ZenPolicy.PEOPLE_TYPE_NONE) .allowReminders(policy.allowReminders()) .allowRepeatCallers(policy.allowRepeatCallers()) @@ -65,9 +69,58 @@ class ZenAdapters { return zenPolicyBuilder.build(); } + /** Maps {@link ZenPolicy.PeopleType} enum to {@link Policy.PrioritySenders}. */ + @Policy.PrioritySenders + public static int zenPolicyPeopleTypeToNotificationPolicySenders( + @ZenPolicy.PeopleType int zpPeopleType, @Policy.PrioritySenders int defaultResult) { + switch (zpPeopleType) { + case ZenPolicy.PEOPLE_TYPE_ANYONE: + return Policy.PRIORITY_SENDERS_ANY; + case ZenPolicy.PEOPLE_TYPE_CONTACTS: + return Policy.PRIORITY_SENDERS_CONTACTS; + case ZenPolicy.PEOPLE_TYPE_STARRED: + return Policy.PRIORITY_SENDERS_STARRED; + default: + return defaultResult; + } + } + + /** Maps {@link Policy.PrioritySenders} enum to {@link ZenPolicy.PeopleType}. */ + @ZenPolicy.PeopleType + public static int notificationPolicySendersToZenPolicyPeopleType( + @Policy.PrioritySenders int npPrioritySenders) { + switch (npPrioritySenders) { + case Policy.PRIORITY_SENDERS_ANY: + return ZenPolicy.PEOPLE_TYPE_ANYONE; + case Policy.PRIORITY_SENDERS_CONTACTS: + return ZenPolicy.PEOPLE_TYPE_CONTACTS; + case Policy.PRIORITY_SENDERS_STARRED: + default: + return ZenPolicy.PEOPLE_TYPE_STARRED; + } + } + + /** Maps {@link ZenPolicy.ConversationSenders} enum to {@link Policy.ConversationSenders}. */ + @Policy.ConversationSenders + public static int zenPolicyConversationSendersToNotificationPolicy( + @ZenPolicy.ConversationSenders int zpConversationSenders, + @Policy.ConversationSenders int defaultResult) { + switch (zpConversationSenders) { + case ZenPolicy.CONVERSATION_SENDERS_ANYONE: + return Policy.CONVERSATION_SENDERS_ANYONE; + case ZenPolicy.CONVERSATION_SENDERS_IMPORTANT: + return Policy.CONVERSATION_SENDERS_IMPORTANT; + case ZenPolicy.CONVERSATION_SENDERS_NONE: + return Policy.CONVERSATION_SENDERS_NONE; + default: + return defaultResult; + } + } + + /** Maps {@link Policy.ConversationSenders} enum to {@link ZenPolicy.ConversationSenders}. */ @ZenPolicy.ConversationSenders private static int notificationPolicyConversationSendersToZenPolicy( - int npPriorityConversationSenders) { + @Policy.ConversationSenders int npPriorityConversationSenders) { switch (npPriorityConversationSenders) { case Policy.CONVERSATION_SENDERS_ANYONE: return ZenPolicy.CONVERSATION_SENDERS_ANYONE; @@ -75,8 +128,7 @@ class ZenAdapters { return ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; case Policy.CONVERSATION_SENDERS_NONE: return ZenPolicy.CONVERSATION_SENDERS_NONE; - case Policy.CONVERSATION_SENDERS_UNSET: - default: + default: // including Policy.CONVERSATION_SENDERS_UNSET return ZenPolicy.CONVERSATION_SENDERS_UNSET; } } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index d9ca935b35b3..1d6dd1ebd54a 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -24,6 +24,9 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; +import static android.service.notification.ZenAdapters.notificationPolicySendersToZenPolicyPeopleType; +import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy; +import static android.service.notification.ZenAdapters.zenPolicyPeopleTypeToNotificationPolicySenders; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -1269,11 +1272,11 @@ public class ZenModeConfig implements Parcelable { public ZenPolicy toZenPolicy() { ZenPolicy.Builder builder = new ZenPolicy.Builder() .allowCalls(allowCalls - ? ZenModeConfig.getZenPolicySenders(allowCallsFrom) + ? notificationPolicySendersToZenPolicyPeopleType(allowCallsFrom) : ZenPolicy.PEOPLE_TYPE_NONE) .allowRepeatCallers(allowRepeatCallers) .allowMessages(allowMessages - ? ZenModeConfig.getZenPolicySenders(allowMessagesFrom) + ? notificationPolicySendersToZenPolicyPeopleType(allowMessagesFrom) : ZenPolicy.PEOPLE_TYPE_NONE) .allowReminders(allowReminders) .allowEvents(allowEvents) @@ -1333,14 +1336,14 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; - messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityMessageSenders(), - messageSenders); + messageSenders = zenPolicyPeopleTypeToNotificationPolicySenders( + zenPolicy.getPriorityMessageSenders(), messageSenders); } if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; - conversationSenders = getConversationSendersWithDefault( + conversationSenders = zenPolicyConversationSendersToNotificationPolicy( zenPolicy.getPriorityConversationSenders(), conversationSenders); } else { conversationSenders = CONVERSATION_SENDERS_NONE; @@ -1349,8 +1352,8 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; - callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders(), - callSenders); + callSenders = zenPolicyPeopleTypeToNotificationPolicySenders( + zenPolicy.getPriorityCallSenders(), callSenders); } if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, @@ -1449,47 +1452,6 @@ public class ZenModeConfig implements Parcelable { return (policy.suppressedVisualEffects & visualEffect) == 0; } - private static int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders, - int defaultPolicySender) { - switch (senders) { - case ZenPolicy.PEOPLE_TYPE_ANYONE: - return Policy.PRIORITY_SENDERS_ANY; - case ZenPolicy.PEOPLE_TYPE_CONTACTS: - return Policy.PRIORITY_SENDERS_CONTACTS; - case ZenPolicy.PEOPLE_TYPE_STARRED: - return Policy.PRIORITY_SENDERS_STARRED; - default: - return defaultPolicySender; - } - } - - private static int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders, - int defaultPolicySender) { - switch (senders) { - case ZenPolicy.CONVERSATION_SENDERS_ANYONE: - case ZenPolicy.CONVERSATION_SENDERS_IMPORTANT: - case ZenPolicy.CONVERSATION_SENDERS_NONE: - return senders; - default: - return defaultPolicySender; - } - } - - /** - * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType - */ - public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) { - switch (senders) { - case Policy.PRIORITY_SENDERS_ANY: - return ZenPolicy.PEOPLE_TYPE_ANYONE; - case Policy.PRIORITY_SENDERS_CONTACTS: - return ZenPolicy.PEOPLE_TYPE_CONTACTS; - case Policy.PRIORITY_SENDERS_STARRED: - default: - return ZenPolicy.PEOPLE_TYPE_STARRED; - } - } - public Policy toNotificationPolicy() { int priorityCategories = 0; int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS; @@ -1524,7 +1486,7 @@ public class ZenModeConfig implements Parcelable { } priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); - priorityConversationSenders = getConversationSendersWithDefault( + priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( allowConversationsFrom, priorityConversationSenders); int state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0; @@ -1559,15 +1521,6 @@ public class ZenModeConfig implements Parcelable { } } - private static int prioritySendersToSource(int prioritySenders, int def) { - switch (prioritySenders) { - case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT; - case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR; - case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE; - default: return def; - } - } - private static int normalizePrioritySenders(int prioritySenders, int def) { if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS || prioritySenders == Policy.PRIORITY_SENDERS_STARRED diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl index bbb4bc6c0272..e44c69c4df28 100644 --- a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl +++ b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl @@ -26,6 +26,7 @@ import android.app.ondeviceintelligence.IFeatureCallback; import android.app.ondeviceintelligence.IListFeaturesCallback; import android.app.ondeviceintelligence.IFeatureDetailsCallback; import com.android.internal.infra.AndroidFuture; +import android.service.ondeviceintelligence.IRemoteProcessingService; /** @@ -41,4 +42,5 @@ oneway interface IOnDeviceIntelligenceService { void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future); void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback); void requestFeatureDownload(in Feature feature, in ICancellationSignal cancellationSignal, in IDownloadCallback downloadCallback); + void registerRemoteServices(in IRemoteProcessingService remoteProcessingService); }
\ No newline at end of file diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl index 08eb9278fcc4..e3fda04e6592 100644 --- a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl +++ b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl @@ -23,8 +23,10 @@ import android.app.ondeviceintelligence.IProcessingSignal; import android.app.ondeviceintelligence.Content; import android.app.ondeviceintelligence.Feature; import android.os.ICancellationSignal; +import android.os.PersistableBundle; +import android.os.Bundle; import android.service.ondeviceintelligence.IRemoteStorageService; - +import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback; /** * Interface for a concrete implementation to provide on device trusted inference. @@ -41,4 +43,6 @@ oneway interface IOnDeviceTrustedInferenceService { void processRequestStreaming(in Feature feature, in Content request, in int requestType, in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal, in IStreamingResponseCallback callback); + void updateProcessingState(in Bundle processingState, + in IProcessingUpdateStatusCallback callback); }
\ No newline at end of file diff --git a/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl new file mode 100644 index 000000000000..7ead8690abb4 --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl @@ -0,0 +1,14 @@ +package android.service.ondeviceintelligence; + +import android.os.PersistableBundle; + +/** + * Interface for receiving status from a updateProcessingState call from on-device intelligence + * service. + * + * @hide + */ +interface IProcessingUpdateStatusCallback { + void onSuccess(in PersistableBundle statusParams) = 1; + void onFailure(int errorCode, in String errorMessage) = 2; +} diff --git a/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl b/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl new file mode 100644 index 000000000000..32a8a6a70406 --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.ondeviceintelligence; + +import android.os.Bundle; +import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback; + + +/** + * Interface for a concrete implementation to provide methods to update state of a remote service. + * + * @hide + */ +oneway interface IRemoteProcessingService { + void updateProcessingState(in Bundle processingState, + in IProcessingUpdateStatusCallback callback); +} diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java index 0cba1d37721a..46ba25d40bec 100644 --- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java +++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java @@ -18,6 +18,7 @@ package android.service.ondeviceintelligence; import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; +import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -47,11 +48,18 @@ import android.util.Slog; import com.android.internal.infra.AndroidFuture; +import androidx.annotation.IntDef; + import java.io.File; import java.io.FileNotFoundException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.LongConsumer; @@ -64,6 +72,10 @@ import java.util.function.LongConsumer; * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is * returned. * + * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are + * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended + * that implementations of this system-service expose this API to the clients via a library which + * has more defined contract.</p> * <pre> * {@literal * <service android:name=".SampleOnDeviceIntelligenceService" @@ -78,6 +90,8 @@ import java.util.function.LongConsumer; public abstract class OnDeviceIntelligenceService extends Service { private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName(); + private volatile IRemoteProcessingService mRemoteProcessingService; + /** * The {@link Intent} that must be declared as handled by the service. To be supported, the * service must also require the @@ -88,6 +102,7 @@ public abstract class OnDeviceIntelligenceService extends Service { public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService"; + /** * @hide */ @@ -167,12 +182,58 @@ public abstract class OnDeviceIntelligenceService extends Service { remoteCallback.sendResult(bundle); }); } + + @Override + public void registerRemoteServices( + IRemoteProcessingService remoteProcessingService) { + mRemoteProcessingService = remoteProcessingService; + } }; } Slog.w(TAG, "Incorrect service interface, returning null."); return null; } + /** + * Invoked by the {@link OnDeviceIntelligenceService} inorder to send updates to the inference + * service if there is a state change to be performed. + * + * @param processingState the updated state to be applied. + * @param callbackExecutor executor to the run status callback on. + * @param statusReceiver receiver to get status of the update state operation. + */ + public final void updateProcessingState(@NonNull Bundle processingState, + @NonNull @CallbackExecutor Executor callbackExecutor, + @NonNull OutcomeReceiver<PersistableBundle, OnDeviceUpdateProcessingException> statusReceiver) { + Objects.requireNonNull(callbackExecutor); + if (mRemoteProcessingService == null) { + throw new IllegalStateException("Remote processing service is unavailable."); + } + try { + mRemoteProcessingService.updateProcessingState(processingState, + new IProcessingUpdateStatusCallback.Stub() { + @Override + public void onSuccess(PersistableBundle result) { + Binder.withCleanCallingIdentity(() -> { + callbackExecutor.execute( + () -> statusReceiver.onResult(result)); + }); + } + + @Override + public void onFailure(int errorCode, String errorMessage) { + Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( + () -> statusReceiver.onError( + new OnDeviceUpdateProcessingException( + errorCode, errorMessage)))); + } + }); + } catch (RemoteException e) { + Slog.e(TAG, "Error in updateProcessingState: " + e); + throw new RuntimeException(e); + } + } + private OutcomeReceiver<Feature, OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureCallback( IFeatureCallback featureCallback) { @@ -197,7 +258,6 @@ public abstract class OnDeviceIntelligenceService extends Service { } } }; - } private OutcomeReceiver<List<Feature>, @@ -380,4 +440,60 @@ public abstract class OnDeviceIntelligenceService extends Service { * @param versionConsumer consumer to populate the version. */ public abstract void onGetVersion(@NonNull LongConsumer versionConsumer); + + + /** + * Exception type to be populated when calls to {@link #updateProcessingState} fail. + */ + public static class OnDeviceUpdateProcessingException extends + OnDeviceIntelligenceServiceException { + /** + * The connection to remote service failed and the processing state could not be updated. + */ + public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 1; + + + /** + * @hide + */ + @IntDef(value = { + PROCESSING_UPDATE_STATUS_CONNECTION_FAILED + }, open = true) + @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, + ElementType.FIELD}) + @Retention(RetentionPolicy.SOURCE) + public @interface ErrorCode { + } + + public OnDeviceUpdateProcessingException(@ErrorCode int errorCode) { + super(errorCode); + } + + public OnDeviceUpdateProcessingException(@ErrorCode int errorCode, + @NonNull String errorMessage) { + super(errorCode, errorMessage); + } + } + + /** + * Exception type to be used for surfacing errors to service implementation. + */ + public abstract static class OnDeviceIntelligenceServiceException extends Exception { + private final int mErrorCode; + + public OnDeviceIntelligenceServiceException(int errorCode) { + this.mErrorCode = errorCode; + } + + public OnDeviceIntelligenceServiceException(int errorCode, + @NonNull String errorMessage) { + super(errorMessage); + this.mErrorCode = errorCode; + } + + public int getErrorCode() { + return mErrorCode; + } + + } } diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java index 96982e3d7829..86001975cc09 100644 --- a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java +++ b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java @@ -36,13 +36,16 @@ import android.app.ondeviceintelligence.ProcessingSignal; import android.app.ondeviceintelligence.StreamingResponseReceiver; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.CancellationSignal; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteException; +import android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException; import android.util.Log; import android.util.Slog; @@ -65,6 +68,11 @@ import java.util.function.Consumer; * non-streaming fashion. Also, provides a way to register a storage service that will be used to * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p> * + * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are + * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended + * that implementations of this system-service expose this API to the clients via a library which + * has more defined contract.</p> + * * <pre> * {@literal * <service android:name=".SampleTrustedInferenceService" @@ -152,6 +160,17 @@ public abstract class OnDeviceTrustedInferenceService extends Service { wrapResponseCallback(callback) ); } + + @Override + public void updateProcessingState(Bundle processingState, + IProcessingUpdateStatusCallback callback) { + Objects.requireNonNull(processingState); + Objects.requireNonNull(callback); + + OnDeviceTrustedInferenceService.this.onUpdateProcessingState(processingState, + wrapOutcomeReceiver(callback) + ); + } }; } Slog.w(TAG, "Incorrect service interface, returning null."); @@ -233,6 +252,21 @@ public abstract class OnDeviceTrustedInferenceService extends Service { @NonNull OutcomeReceiver<Content, OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback); + + /** + * Invoked when processing environment needs to be updated or refreshed with fresh + * configuration, files or state. + * + * @param processingState contains updated state and params that are to be applied to the + * processing environmment, + * @param callback callback to populate the update status and if there are params + * associated with the status. + */ + public abstract void onUpdateProcessingState(@NonNull Bundle processingState, + @NonNull OutcomeReceiver<PersistableBundle, + OnDeviceUpdateProcessingException> callback); + + /** * Overrides {@link Context#openFileInput} to read files with the given file names under the * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in @@ -407,4 +441,31 @@ public abstract class OnDeviceTrustedInferenceService extends Service { } }; } + + @NonNull + private static OutcomeReceiver<PersistableBundle, OnDeviceUpdateProcessingException> wrapOutcomeReceiver( + IProcessingUpdateStatusCallback callback) { + return new OutcomeReceiver<>() { + @Override + public void onResult(@NonNull PersistableBundle result) { + try { + callback.onSuccess(result); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending result: " + e); + + } + } + + @Override + public void onError( + @androidx.annotation.NonNull OnDeviceUpdateProcessingException error) { + try { + callback.onFailure(error.getErrorCode(), error.getMessage()); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending exception details: " + e); + } + } + }; + } + } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 20adc5427495..306410c9a98b 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -519,7 +519,7 @@ public class VoiceInteractionService extends Service { @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale, @NonNull @CallbackExecutor Executor executor, @NonNull AlwaysOnHotwordDetector.Callback callback) { - // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning + // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning Objects.requireNonNull(keyphrase); Objects.requireNonNull(locale); @@ -545,10 +545,6 @@ public class VoiceInteractionService extends Service { @NonNull SoundTrigger.ModuleProperties moduleProperties, @NonNull @CallbackExecutor Executor executor, @NonNull AlwaysOnHotwordDetector.Callback callback) { - // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the - // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale, - // SoundTrigger.ModuleProperties, AlwaysOnHotwordDetector.Callback)} and replace with the - // permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched. Objects.requireNonNull(keyphrase); Objects.requireNonNull(locale); @@ -615,11 +611,6 @@ public class VoiceInteractionService extends Service { @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) { - // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the - // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory, - // AlwaysOnHotwordDetector.Callback)} and replace with the permission - // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched. - return createAlwaysOnHotwordDetectorInternal(keyphrase, locale, /* supportHotwordDetectionService= */ true, options, sharedMemory, /* modulProperties */ null, /* executor= */ null, callback); @@ -671,11 +662,7 @@ public class VoiceInteractionService extends Service { @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @NonNull @CallbackExecutor Executor executor, @NonNull AlwaysOnHotwordDetector.Callback callback) { - // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning - // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the - // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory, - // Executor, AlwaysOnHotwordDetector.Callback)} and replace with the permission - // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched. + // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning Objects.requireNonNull(keyphrase); Objects.requireNonNull(locale); @@ -702,10 +689,6 @@ public class VoiceInteractionService extends Service { @NonNull SoundTrigger.ModuleProperties moduleProperties, @NonNull @CallbackExecutor Executor executor, @NonNull AlwaysOnHotwordDetector.Callback callback) { - // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the - // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale, PersistableBundle, - // SharedMemory, SoundTrigger.ModuleProperties, Executor, AlwaysOnHotwordDetector.Callback)} - // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched. Objects.requireNonNull(keyphrase); Objects.requireNonNull(locale); diff --git a/core/java/android/view/ScrollFeedbackProvider.java b/core/java/android/view/ScrollFeedbackProvider.java index 8a44d4fa5934..798203fadb4d 100644 --- a/core/java/android/view/ScrollFeedbackProvider.java +++ b/core/java/android/view/ScrollFeedbackProvider.java @@ -21,8 +21,11 @@ import android.annotation.NonNull; import android.view.flags.Flags; /** - * Interface to represent an entity giving consistent feedback for different events surrounding view - * scroll. + * Provides feedback to the user for scroll events on a {@link View}. The type of feedback provided + * to the user may depend on the {@link InputDevice} that generated the scroll events. + * + * <p>An example of the type of feedback that this interface may provide is haptic feedback (that + * is, tactile feedback that provide the user physical feedback for their scroll). * * <p>The interface provides methods for the client to report different scroll events. The client * should report all scroll events that they want to be considered for scroll feedback using the diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 708751a25053..a7cb1696e668 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1210,6 +1210,9 @@ public final class ViewRootImpl implements ViewParent, mSensitiveContentProtectionService = ISensitiveContentProtectionManager.Stub.asInterface( ServiceManager.getService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE)); + if (mSensitiveContentProtectionService == null) { + Log.e(TAG, "SensitiveContentProtectionService shouldn't be null"); + } } else { mSensitiveContentProtectionService = null; } @@ -4179,6 +4182,9 @@ public final class ViewRootImpl implements ViewParent, */ void notifySensitiveContentAppProtection(boolean showSensitiveContent) { try { + if (mSensitiveContentProtectionService == null) { + return; + } // The window would be blocked during screen share if it shows sensitive content. mSensitiveContentProtectionService.setSensitiveContentProtection( getWindowToken(), mContext.getPackageName(), showSensitiveContent); diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index 7f796611d217..d992febc375e 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -182,7 +182,6 @@ public interface ImeTracker { PHASE_CLIENT_ANIMATION_FINISHED_SHOW, PHASE_CLIENT_ANIMATION_FINISHED_HIDE, PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT, - PHASE_CLIENT_ANIMATION_FINISHED_HIDE, PHASE_IME_SHOW_WINDOW, PHASE_IME_HIDE_WINDOW, PHASE_IME_PRIVILEGED_OPERATIONS, diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java index 3fc0a305c8e6..8501474b70a6 100644 --- a/core/java/android/webkit/WebViewDelegate.java +++ b/core/java/android/webkit/WebViewDelegate.java @@ -175,8 +175,16 @@ public final class WebViewDelegate { /** * Adds the WebView asset path to {@link android.content.res.AssetManager}. + * If {@link android.content.res.Flags#FLAG_REGISTER_RESOURCE_PATHS} is enabled, this function + * will be a no-op because the asset paths appending work will only be handled by + * {@link android.content.res.Resources#registerResourcePaths(String, ApplicationInfo)}, + * otherwise it behaves the old way. */ public void addWebViewAssetPath(Context context) { + if (android.content.res.Flags.registerResourcePaths()) { + return; + } + final String[] newAssetPaths = WebViewFactory.getLoadedPackageInfo().applicationInfo.getAllApkPaths(); final ApplicationInfo appInfo = context.getApplicationInfo(); diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index c748a57dce74..01fdd1d4038c 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.res.Resources; import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; @@ -544,8 +545,14 @@ public final class WebViewFactory { Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()"); try { sTimestamps.mAddAssetsStart = SystemClock.uptimeMillis(); - for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) { - initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath); + if (android.content.res.Flags.registerResourcePaths()) { + Resources.registerResourcePaths(webViewContext.getPackageName(), + webViewContext.getApplicationInfo()); + } else { + for (String newAssetPath : webViewContext.getApplicationInfo() + .getAllApkPaths()) { + initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath); + } } sTimestamps.mAddAssetsEnd = sTimestamps.mGetClassLoaderStart = SystemClock.uptimeMillis(); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 0e5747d0e445..fe4ac4e2f0f1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1105,6 +1105,7 @@ public class RemoteViews implements Parcelable, Filter { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { mViewId = parcel.readInt(); mIntentId = parcel.readInt(); + mIsReplacedIntoAction = parcel.readBoolean(); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); mItems = mServiceIntent != null ? null @@ -1128,6 +1129,7 @@ public class RemoteViews implements Parcelable, Filter { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewId); dest.writeInt(mIntentId); + dest.writeBoolean(mIsReplacedIntoAction); dest.writeTypedObject(mServiceIntent, flags); if (mItems != null) { mItems.writeToParcel(dest, flags, /* attached= */ true); @@ -1209,6 +1211,19 @@ public class RemoteViews implements Parcelable, Filter { } /** + * The maximum size for RemoteViews with converted RemoteCollectionItemsAdapter. + * When converting RemoteViewsAdapter to RemoteCollectionItemsAdapter, we want to put size + * limits on each unique RemoteCollectionItems in order to not exceed the transaction size limit + * for each parcel (typically 1 MB). We leave a certain ratio of the maximum size as a buffer + * for missing calculations of certain parameters (e.g. writing a RemoteCollectionItems to the + * parcel will write its Id array as well, but that is missing when writing itschild RemoteViews + * directly to the parcel as we did in RemoteViewsService) + * + * @hide + */ + private static final int MAX_SINGLE_PARCEL_SIZE = (int) (1_000_000 * 0.8); + + /** * @hide */ public CompletableFuture<Void> collectAllIntents() { @@ -1260,17 +1275,47 @@ public class RemoteViews implements Parcelable, Filter { return mUriToCollectionMapping.get(uri); } - CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) { - CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null); + public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete( + @NonNull RemoteViews inViews) { + SparseArray<Intent> idToIntentMapping = new SparseArray<>(); + // Collect the number of uinque Intent (which is equal to the number of new connections + // to make) for size allocation and exclude certain collections from being written to + // the parcel to better estimate the space left for reallocation. + collectAllIntentsInternal(inViews, idToIntentMapping); + + // Calculate the individual size here + int numOfIntents = idToIntentMapping.size(); + if (numOfIntents == 0) { + Log.e(LOG_TAG, "Possibly notifying updates for nonexistent view Id"); + return CompletableFuture.completedFuture(null); + } + + Parcel sizeTestParcel = Parcel.obtain(); + // Write self RemoteViews to the parcel, which includes the actions/bitmaps/collection + // cache to see how much space is left for the RemoteCollectionItems that are to be + // updated. + RemoteViews.this.writeToParcel(sizeTestParcel, + /* flags= */ 0, + /* intentsToIgnore= */ idToIntentMapping); + int remainingSize = MAX_SINGLE_PARCEL_SIZE - sizeTestParcel.dataSize(); + sizeTestParcel.recycle(); + + int individualSize = remainingSize < 0 + ? 0 + : remainingSize / numOfIntents; + + return connectAllUniqueIntents(individualSize, idToIntentMapping); + } + + private void collectAllIntentsInternal(@NonNull RemoteViews inViews, + @NonNull SparseArray<Intent> idToIntentMapping) { if (inViews.hasSizedRemoteViews()) { for (RemoteViews remoteViews : inViews.mSizedRemoteViews) { - collectionFuture = CompletableFuture.allOf(collectionFuture, - collectAllIntentsNoComplete(remoteViews)); + collectAllIntentsInternal(remoteViews, idToIntentMapping); } } else if (inViews.hasLandscapeAndPortraitLayouts()) { - collectionFuture = CompletableFuture.allOf( - collectAllIntentsNoComplete(inViews.mLandscape), - collectAllIntentsNoComplete(inViews.mPortrait)); + collectAllIntentsInternal(inViews.mLandscape, idToIntentMapping); + collectAllIntentsInternal(inViews.mPortrait, idToIntentMapping); } else if (inViews.mActions != null) { for (Action action : inViews.mActions) { if (action instanceof SetRemoteCollectionItemListAdapterAction rca) { @@ -1280,13 +1325,16 @@ public class RemoteViews implements Parcelable, Filter { } if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) { - final String uri = mIdToUriMapping.get(rca.mIntentId); - collectionFuture = CompletableFuture.allOf(collectionFuture, - getItemsFutureFromIntentWithTimeout(rca.mServiceIntent) - .thenAccept(rc -> { - rc.setHierarchyRootData(getHierarchyRootData()); - mUriToCollectionMapping.put(uri, rc); - })); + rca.mIsReplacedIntoAction = false; + + // Avoid redundant connections for the same intent. Also making sure + // that the number of connections we are making is always equal to the + // nmuber of unique intents that are being used for the updates. + if (idToIntentMapping.contains(rca.mIntentId)) { + continue; + } + + idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent); rca.mItems = null; continue; } @@ -1295,7 +1343,7 @@ public class RemoteViews implements Parcelable, Filter { // intents. if (rca.mServiceIntent != null) { final String uri = rca.mServiceIntent.toUri(0); - int index = mIdToUriMapping.indexOfValue(uri); + int index = mIdToUriMapping.indexOfValueByValue(uri); if (index == -1) { int newIntentId = mIdToUriMapping.size(); rca.mIntentId = newIntentId; @@ -1305,41 +1353,50 @@ public class RemoteViews implements Parcelable, Filter { rca.mItems = null; continue; } - collectionFuture = CompletableFuture.allOf(collectionFuture, - getItemsFutureFromIntentWithTimeout(rca.mServiceIntent) - .thenAccept(rc -> { - rc.setHierarchyRootData(getHierarchyRootData()); - mUriToCollectionMapping.put(uri, rc); - })); + + idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent); rca.mItems = null; } else { for (RemoteViews views : rca.mItems.mViews) { - collectionFuture = CompletableFuture.allOf(collectionFuture, - collectAllIntentsNoComplete(views)); + collectAllIntentsInternal(views, idToIntentMapping); } } } else if (action instanceof ViewGroupActionAdd vgaa && vgaa.mNestedViews != null) { - collectionFuture = CompletableFuture.allOf(collectionFuture, - collectAllIntentsNoComplete(vgaa.mNestedViews)); + collectAllIntentsInternal(vgaa.mNestedViews, idToIntentMapping); } } } + } - return collectionFuture; + private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize, + @NonNull SparseArray<Intent> idToIntentMapping) { + List<CompletableFuture<Void>> intentFutureList = new ArrayList<>(); + for (int i = 0; i < idToIntentMapping.size(); i++) { + String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i)); + Intent currentIntent = idToIntentMapping.valueAt(i); + intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent, + individualSize) + .thenAccept(items -> { + items.setHierarchyRootData(getHierarchyRootData()); + mUriToCollectionMapping.put(currentIntentUri, items); + })); + } + + return CompletableFuture.allOf(intentFutureList.toArray(CompletableFuture[]::new)); } private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout( - Intent intent) { + Intent intent, int individualSize) { if (intent == null) { Log.e(LOG_TAG, "Null intent received when generating adapter future"); return CompletableFuture.completedFuture(new RemoteCollectionItems - .Builder().build()); + .Builder().build()); } final Context context = ActivityThread.currentApplication(); - final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); + final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE), result.defaultExecutor(), new ServiceConnection() { @Override @@ -1348,11 +1405,11 @@ public class RemoteViews implements Parcelable, Filter { RemoteCollectionItems items; try { items = IRemoteViewsFactory.Stub.asInterface(iBinder) - .getRemoteCollectionItems(); + .getRemoteCollectionItems(individualSize); } catch (RemoteException re) { items = new RemoteCollectionItems.Builder().build(); - Log.e(LOG_TAG, "Error getting collection items from the factory", - re); + Log.e(LOG_TAG, "Error getting collection items from the" + + " factory", re); } finally { context.unbindService(this); } @@ -1371,10 +1428,17 @@ public class RemoteViews implements Parcelable, Filter { return result; } - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(Parcel out, int flags, + @Nullable SparseArray<Intent> intentsToIgnore) { out.writeInt(mIdToUriMapping.size()); for (int i = 0; i < mIdToUriMapping.size(); i++) { - out.writeInt(mIdToUriMapping.keyAt(i)); + int currentIntentId = mIdToUriMapping.keyAt(i); + if (intentsToIgnore != null && intentsToIgnore.contains(currentIntentId)) { + // Skip writing collections that are to be updated in the following steps to + // better estimate the RemoteViews size. + continue; + } + out.writeInt(currentIntentId); String intentUri = mIdToUriMapping.valueAt(i); out.writeString8(intentUri); mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true); @@ -6724,7 +6788,13 @@ public class RemoteViews implements Parcelable, Filter { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { + writeToParcel(dest, flags, /* intentsToIgnore= */ null); + } + + private void writeToParcel(Parcel dest, int flags, + @Nullable SparseArray<Intent> intentsToIgnore) { boolean prevSquashingAllowed = dest.allowSquashing(); if (!hasMultipleLayouts()) { @@ -6733,7 +6803,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); - mCollectionCache.writeToParcel(dest, flags); + mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } mApplication.writeToParcel(dest, flags); if (mIsRoot || mIdealSize == null) { @@ -6750,7 +6820,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); - mCollectionCache.writeToParcel(dest, flags); + mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } dest.writeInt(mSizedRemoteViews.size()); for (RemoteViews view : mSizedRemoteViews) { @@ -6762,7 +6832,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); - mCollectionCache.writeToParcel(dest, flags); + mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java index a250a867b9de..d4a5bbd79d31 100644 --- a/core/java/android/widget/RemoteViewsService.java +++ b/core/java/android/widget/RemoteViewsService.java @@ -19,6 +19,7 @@ package android.widget; import android.app.Service; import android.content.Intent; import android.os.IBinder; +import android.os.Parcel; import com.android.internal.widget.IRemoteViewsFactory; @@ -43,13 +44,6 @@ public abstract class RemoteViewsService extends Service { private static final Object sLock = new Object(); /** - * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory - * - * @hide - */ - private static final int MAX_NUM_ENTRY = 10; - - /** * An interface for an adapter between a remote collection view (ListView, GridView, etc) and * the underlying data for that view. The implementor is responsible for making a RemoteView * for each item in the data set. This interface is a thin wrapper around {@link Adapter}. @@ -235,9 +229,10 @@ public abstract class RemoteViewsService extends Service { } @Override - public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { + public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) { RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems .Builder().build(); + Parcel capSizeTestParcel = Parcel.obtain(); try { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = @@ -245,15 +240,25 @@ public abstract class RemoteViewsService extends Service { mFactory.onDataSetChanged(); itemsBuilder.setHasStableIds(mFactory.hasStableIds()); - final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY); + final int numOfEntries = mFactory.getCount(); + for (int i = 0; i < numOfEntries; i++) { - itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i)); + final long currentItemId = mFactory.getItemId(i); + final RemoteViews currentView = mFactory.getViewAt(i); + currentView.writeToParcel(capSizeTestParcel, 0); + if (capSizeTestParcel.dataSize() > capSize) { + break; + } + itemsBuilder.addItem(currentItemId, currentView); } items = itemsBuilder.build(); } catch (Exception ex) { Thread t = Thread.currentThread(); Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); + } finally { + // Recycle the parcel + capSizeTestParcel.recycle(); } return items; } diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 65b59790e327..c7695187e52d 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -172,6 +172,20 @@ public class IntentForwarderActivity extends Activity { newIntent.prepareToLeaveUser(callingUserId); final CompletableFuture<ResolveInfo> targetResolveInfoFuture = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, targetUserId); + + if (isPrivateProfile(callingUserId)) { + buildAndExecuteForPrivateProfile(intentReceived, className, newIntent, callingUserId, + targetUserId); + } else { + buildAndExecute(targetResolveInfoFuture, intentReceived, className, newIntent, + callingUserId, + targetUserId, userMessage, managedProfile); + } + } + + private void buildAndExecute(CompletableFuture<ResolveInfo> targetResolveInfoFuture, + Intent intentReceived, String className, Intent newIntent, int callingUserId, + int targetUserId, String userMessage, UserInfo managedProfile) { targetResolveInfoFuture .thenApplyAsync(targetResolveInfo -> { if (isResolverActivityResolveInfo(targetResolveInfo)) { @@ -195,6 +209,23 @@ public class IntentForwarderActivity extends Activity { }, getApplicationContext().getMainExecutor()); } + private void buildAndExecuteForPrivateProfile( + Intent intentReceived, String className, Intent newIntent, int callingUserId, + int targetUserId) { + final CompletableFuture<ResolveInfo> targetResolveInfoFuture = + mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, targetUserId); + targetResolveInfoFuture + .thenAcceptAsync(targetResolveInfo -> { + if (isResolverActivityResolveInfo(targetResolveInfo)) { + launchResolverActivityWithCorrectTab(intentReceived, className, newIntent, + callingUserId, targetUserId); + } else { + maybeShowUserConsentMiniResolverPrivate(targetResolveInfo, newIntent, + targetUserId); + } + }, getApplicationContext().getMainExecutor()); + } + private void maybeShowUserConsentMiniResolver( ResolveInfo target, Intent launchIntent, UserInfo managedProfile) { if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) { @@ -233,24 +264,70 @@ public class IntentForwarderActivity extends Activity { "Showing user consent for redirection into the managed profile for intent [%s] and " + " calling package [%s]", launchIntent, callingPackage)); - int layoutId = R.layout.miniresolver; - setContentView(layoutId); + PackageManager packageManagerForTargetUser = + createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0) + .getPackageManager(); + buildMiniResolver(target, launchIntent, targetUserId, + getOpenInWorkMessage(launchIntent, target.loadLabel(packageManagerForTargetUser)), + packageManagerForTargetUser); - findViewById(R.id.title_container).setElevation(0); + View telephonyInfo = findViewById(R.id.miniresolver_info_section); + + // Additional information section is work telephony specific. Therefore, it is only shown + // for telephony related intents, when all sim subscriptions are in the work profile. + if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent)) + && devicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType() + == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) { + telephonyInfo.setVisibility(View.VISIBLE); + ((TextView) findViewById(R.id.miniresolver_info_section_text)) + .setText(getWorkTelephonyInfoSectionMessage(launchIntent)); + } else { + telephonyInfo.setVisibility(View.GONE); + } + } + + private void maybeShowUserConsentMiniResolverPrivate( + ResolveInfo target, Intent launchIntent, int targetUserId) { + if (target == null || isIntentForwarderResolveInfo(target)) { + finish(); + return; + } + + String callingPackage = getCallingPackage(); + + Log.i("IntentForwarderActivity", String.format( + "Showing user consent for redirection into the main profile for intent [%s] and " + + " calling package [%s]", + launchIntent, callingPackage)); PackageManager packageManagerForTargetUser = createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0) .getPackageManager(); + buildMiniResolver(target, launchIntent, targetUserId, + getString(R.string.miniresolver_open_in_personal, + target.loadLabel(packageManagerForTargetUser)), + packageManagerForTargetUser); + + View telephonyInfo = findViewById(R.id.miniresolver_info_section); + telephonyInfo.setVisibility(View.GONE); + } + + private void buildMiniResolver(ResolveInfo target, Intent launchIntent, int targetUserId, + String resolverTitle, PackageManager pmForTargetUser) { + int layoutId = R.layout.miniresolver; + setContentView(layoutId); + + findViewById(R.id.title_container).setElevation(0); ImageView icon = findViewById(R.id.icon); icon.setImageDrawable( - getAppIcon(target, launchIntent, targetUserId, packageManagerForTargetUser)); + getAppIcon(target, launchIntent, targetUserId, pmForTargetUser)); View buttonContainer = findViewById(R.id.button_bar_container); buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom()); ((TextView) findViewById(R.id.open_cross_profile)).setText( - getOpenInWorkMessage(launchIntent, target.loadLabel(packageManagerForTargetUser))); + resolverTitle); // The mini-resolver's negative button is reused in this flow to cancel the intent ((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel); @@ -269,21 +346,6 @@ public class IntentForwarderActivity extends Activity { targetUserId); finish(); }); - - - View telephonyInfo = findViewById(R.id.miniresolver_info_section); - - // Additional information section is work telephony specific. Therefore, it is only shown - // for telephony related intents, when all sim subscriptions are in the work profile. - if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent)) - && devicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType() - == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) { - telephonyInfo.setVisibility(View.VISIBLE); - ((TextView) findViewById(R.id.miniresolver_info_section_text)) - .setText(getWorkTelephonyInfoSectionMessage(launchIntent)); - } else { - telephonyInfo.setVisibility(View.GONE); - } } private Drawable getAppIcon( @@ -548,6 +610,18 @@ public class IntentForwarderActivity extends Activity { } /** + * Returns the private profile for this device or null if there is no private profile. + */ + @Nullable + private UserInfo getPrivateProfile() { + List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId()); + for (UserInfo userInfo : relatedUsers) { + if (userInfo.isPrivateProfile()) return userInfo; + } + return null; + } + + /** * Returns the userId of the profile parent or UserHandle.USER_NULL if there is * no parent. */ @@ -577,6 +651,17 @@ public class IntentForwarderActivity extends Activity { return mMetricsLogger; } + private boolean isPrivateProfile(int userId) { + UserInfo privateProfile = getPrivateProfile(); + return privateSpaceFlagsEnabled() && privateProfile != null + && privateProfile.id == userId; + } + + private boolean privateSpaceFlagsEnabled() { + return android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enablePrivateSpaceIntentRedirection(); + } + @VisibleForTesting protected Injector createInjector() { return new InjectorImpl(); diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl index 918d9c029ef5..1d0e97295571 100644 --- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl @@ -39,6 +39,6 @@ interface IRemoteViewsFactory { boolean hasStableIds(); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isCreated(); - RemoteViews.RemoteCollectionItems getRemoteCollectionItems(); + RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 240028c191bc..76e71380017c 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -400,6 +400,7 @@ cc_library_shared_for_libandroid_runtime { "libbinary_parse", "libdng_sdk", "libft2", + "libhostgraphics", "libhwui", "libimage_type_recognition", "libjpeg", diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp index d5f17da0a072..071f933db065 100644 --- a/core/jni/LayoutlibLoader.cpp +++ b/core/jni/LayoutlibLoader.cpp @@ -196,14 +196,6 @@ static vector<string> parseCsv(const string& csvString) { return result; } -static vector<string> parseCsv(JNIEnv* env, jstring csvJString) { - const char* charArray = env->GetStringUTFChars(csvJString, 0); - string csvString(charArray); - vector<string> result = parseCsv(csvString); - env->ReleaseStringUTFChars(csvJString, charArray); - return result; -} - void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message) { JNIEnv* env = AndroidRuntime::getJNIEnv(); @@ -395,20 +387,28 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + // Java system properties that contain LayoutLib config. The initial values in the map + // are the default values if the property is not specified. + std::unordered_map<std::string, std::string> systemProperties = + {{"core_native_classes", ""}, + {"register_properties_during_load", ""}, + {"icu.data.path", ""}, + {"use_bridge_for_logging", ""}, + {"keyboard_paths", ""}}; + + for (auto& [name, defaultValue] : systemProperties) { + jstring propertyString = + (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, + env->NewStringUTF(name.c_str()), + env->NewStringUTF(defaultValue.c_str())); + const char* propertyChars = env->GetStringUTFChars(propertyString, 0); + systemProperties[name] = string(propertyChars); + env->ReleaseStringUTFChars(propertyString, propertyChars); + } // Get the names of classes that need to register their native methods - auto nativesClassesJString = - (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, - env->NewStringUTF("core_native_classes"), - env->NewStringUTF("")); - vector<string> classesToRegister = parseCsv(env, nativesClassesJString); - - jstring registerProperty = - (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, - env->NewStringUTF( - "register_properties_during_load"), - env->NewStringUTF("")); - const char* registerPropertyString = env->GetStringUTFChars(registerProperty, 0); - if (strcmp(registerPropertyString, "true") == 0) { + vector<string> classesToRegister = parseCsv(systemProperties["core_native_classes"]); + + if (systemProperties["register_properties_during_load"] == "true") { // Set the system properties first as they could be used in the static initialization of // other classes if (register_android_os_SystemProperties(env) < 0) { @@ -423,35 +423,20 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod); property_initialize_ro_cpu_abilist(); } - env->ReleaseStringUTFChars(registerProperty, registerPropertyString); if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) { return JNI_ERR; } - // Set the location of ICU data - auto stringPath = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, - env->NewStringUTF("icu.data.path"), - env->NewStringUTF("")); - const char* path = env->GetStringUTFChars(stringPath, 0); - - if (strcmp(path, "**n/a**") != 0) { - bool icuInitialized = init_icu(path); + if (!systemProperties["register_properties_during_load"].empty()) { + // Set the location of ICU data + bool icuInitialized = init_icu(systemProperties["icu.data.path"].c_str()); if (!icuInitialized) { - fprintf(stderr, "Failed to initialize ICU\n"); return JNI_ERR; } - } else { - fprintf(stderr, "Skip initializing ICU\n"); } - env->ReleaseStringUTFChars(stringPath, path); - - jstring useJniProperty = - (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, - env->NewStringUTF("use_bridge_for_logging"), - env->NewStringUTF("")); - const char* useJniString = env->GetStringUTFChars(useJniProperty, 0); - if (strcmp(useJniString, "true") == 0) { + + if (systemProperties["use_bridge_for_logging"] == "true") { layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog"); layoutLog = MakeGlobalRefOrDie(env, layoutLog); logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework", @@ -468,23 +453,16 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { // initialize logging, so ANDROD_LOG_TAGS env variable is respected android::base::InitLogging(nullptr, android::base::StderrLogger); } - env->ReleaseStringUTFChars(useJniProperty, useJniString); // Use English locale for number format to ensure correct parsing of floats when using strtof setlocale(LC_NUMERIC, "en_US.UTF-8"); - auto keyboardPathsJString = - (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, - env->NewStringUTF("keyboard_paths"), - env->NewStringUTF("")); - const char* keyboardPathsString = env->GetStringUTFChars(keyboardPathsJString, 0); - if (strcmp(keyboardPathsString, "**n/a**") != 0) { - vector<string> keyboardPaths = parseCsv(env, keyboardPathsJString); + if (!systemProperties["keyboard_paths"].empty()) { + vector<string> keyboardPaths = parseCsv(systemProperties["keyboard_paths"]); init_keyboard(env, keyboardPaths); } else { fprintf(stderr, "Skip initializing keyboard\n"); } - env->ReleaseStringUTFChars(keyboardPathsJString, keyboardPathsString); return JNI_VERSION_1_6; } diff --git a/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto b/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto index 5a18d9e627e8..b75d545b1305 100644 --- a/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto +++ b/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto @@ -39,7 +39,7 @@ message InputMethodManagerServiceProto { optional string cur_token = 14; optional int32 cur_token_display_id = 15; optional bool system_ready = 16; - optional int32 last_switch_user_id = 17; + reserved 17; // deprecated last_switch_user_id optional bool have_connection = 18; optional bool bound_to_method = 19; optional bool is_interactive = 20; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 487b5be9ef5c..ba9751fe1d12 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -8621,6 +8621,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> + <service android:name="com.android.system.virtualmachine.SecretkeeperJobService" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + <service android:name="com.android.server.PruneInstantAppsJobService" android:permission="android.permission.BIND_JOB_SERVICE" > </service> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index d419cee4eef5..7155bf4241e6 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -633,8 +633,8 @@ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"به برنامه اجازه میدهد مجموعه عکستان را تغییر دهد."</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"خواندن مکانها از مجموعه رسانه شما"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"به برنامه اجازه میدهد مکانها را از مجموعه رسانهتان بخواند."</string> - <string name="biometric_app_setting_name" msgid="3339209978734534457">"استفاده از زیستسنجشی"</string> - <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استفاده از زیستسنجشی یا قفل صفحه"</string> + <string name="biometric_app_setting_name" msgid="3339209978734534457">"استفاده از دادههای زیستسنجشی"</string> + <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استفاده از دادههای زیستسنجشی یا قفل صفحه"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شمایید"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"برای ادامه، از زیستسنجشی استفاده کنید"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"برای ادامه، از زیستسنجشی یا قفل صفحه استفاده کنید"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8edf42afa730..90f27311d7c3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3348,26 +3348,23 @@ <string name="config_carrierAppInstallDialogComponent" translatable="false" >com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string> - <!-- Name of the default framework dialog that is used to get or save an app credential. + <!-- Name of the fallback CredentialManager dialog that is used to get or save an app + credential. - This UI should be always launch-able and is used as a fallback when an oem replacement activity - (defined at config_oemCredentialManagerDialogComponent) is undefined / not found. --> - <string name="config_credentialManagerDialogComponent" translatable="false" - >com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string> - <!-- Whether to allow the credential selector activity to be replaced by an activity at - run-time (restricted to the privileged activity specified by - config_credentialSelectorActivityName). + If empty, no fallback will be used. IMPORTANT: In that case the OEM dialog value specified in + config_oemCredentialManagerDialogComponent must always launch-able. Otherwise, the + CredentialManager API contract is broken. - When disabled, the fallback activity defined at - config_credentialManagerDialogComponent will be used instead. --> - <bool name="config_enableOemCredentialManagerDialogComponent" translatable="false">true</bool> + If specified, this UI should be always launch-able. It will be used as a fallback when the OEM + dialog value specified in config_oemCredentialManagerDialogComponent) is undefined / not + found. --> + <string name="config_fallbackCredentialManagerDialogComponent" translatable="false" + >com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string> <!-- Fully qualified activity name providing the credential selector UI, that serves the - CredentialManager APIs. - - Used only when config_enableOemCredentialManagerDialogComponent is true. + CredentialManager APIs. Must be a system app component. - If the activity specified cannot be found or launched, then the fallback activity defined at - config_credentialManagerDialogComponent will be used instead. --> + If empty, or if this activity specified cannot be found or launched, then the fallback activity + defined at config_fallbackCredentialManagerDialogComponent will be used instead. --> <string name="config_oemCredentialManagerDialogComponent" translatable="false"></string> <!-- Name of the broadcast receiver that is used to receive provider change events --> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 5e3e1b0bbb43..6e56fe2abfdd 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -334,4 +334,9 @@ <bool name="config_enable_cellular_on_boot_default">true</bool> <java-symbol type="bool" name="config_enable_cellular_on_boot_default" /> + <!-- Defines metrics pull cooldown period. The default cooldown period is 23 hours, + some Telephony metrics need to be pulled more frequently --> + <integer name="config_metrics_pull_cooldown_millis">82800000</integer> + <java-symbol type="integer" name="config_metrics_pull_cooldown_millis" /> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a1804672da73..a025c8db547f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2297,8 +2297,7 @@ <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" /> <java-symbol type="string" name="config_platformVpnConfirmDialogComponent" /> <java-symbol type="string" name="config_carrierAppInstallDialogComponent" /> - <java-symbol type="string" name="config_credentialManagerDialogComponent" /> - <java-symbol type="bool" name="config_enableOemCredentialManagerDialogComponent" /> + <java-symbol type="string" name="config_fallbackCredentialManagerDialogComponent" /> <java-symbol type="string" name="config_oemCredentialManagerDialogComponent" /> <java-symbol type="string" name="config_credentialManagerReceiverComponent" /> <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index e72beee71e50..24031cad0a3e 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -101,6 +101,7 @@ android_test { "flickerlib-trace_processor_shell", "mockito-target-extended-minus-junit4", "TestParameterInjector", + "android.content.res.flags-aconfig-java", ], libs: [ diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index d115bf306b45..927c67cb1d36 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -21,16 +21,22 @@ import static android.content.Intent.ACTION_EDIT; import static android.content.Intent.ACTION_VIEW; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static com.android.window.flags.Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.annotation.NonNull; @@ -46,6 +52,7 @@ import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; +import android.app.servertransaction.ClientTransactionListenerController; import android.app.servertransaction.ConfigurationChangeItem; import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.ResumeActivityItem; @@ -60,7 +67,9 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.view.Display; @@ -81,11 +90,14 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -103,11 +115,17 @@ public class ActivityThreadTest { // few sequence numbers the framework used to launch the test activity. private static final int BASE_SEQ = 10000000; - @Rule + @Rule(order = 0) public final ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */, false /* launchActivity */); + @Rule(order = 1) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + + @Mock + private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener; + private WindowTokenClientController mOriginalWindowTokenClientController; private Configuration mOriginalAppConfig; @@ -115,6 +133,8 @@ public class ActivityThreadTest { @Before public void setup() { + MockitoAnnotations.initMocks(this); + // Keep track of the original controller, so that it can be used to restore in tearDown() // when there is override in some test cases. mOriginalWindowTokenClientController = WindowTokenClientController.getInstance(); @@ -129,6 +149,8 @@ public class ActivityThreadTest { mCreatedVirtualDisplays = null; } WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController); + ClientTransactionListenerController.getInstance() + .unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener); InstrumentationRegistry.getInstrumentation().runOnMainSync( () -> restoreConfig(ActivityThread.currentActivityThread(), mOriginalAppConfig)); } @@ -783,6 +805,101 @@ public class ActivityThreadTest { verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken); } + @Test + public void testActivityWindowInfoChanged_activityLaunch() { + mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( + mActivityWindowInfoListener); + + final Activity activity = mActivityTestRule.launchActivity(new Intent()); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity); + + verify(mActivityWindowInfoListener).accept(activityClientRecord.token, + activityClientRecord.getActivityWindowInfo()); + } + + @Test + public void testActivityWindowInfoChanged_activityRelaunch() throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( + mActivityWindowInfoListener); + + final Activity activity = mActivityTestRule.launchActivity(new Intent()); + final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); + appThread.scheduleTransaction(newRelaunchResumeTransaction(activity)); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity); + + // The same ActivityWindowInfo won't trigger duplicated callback. + verify(mActivityWindowInfoListener).accept(activityClientRecord.token, + activityClientRecord.getActivityWindowInfo()); + + final Configuration currentConfig = activity.getResources().getConfiguration(); + final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); + activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), + new Rect(0, 0, 1000, 1000)); + final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain( + activity.getActivityToken(), null, null, 0, + new MergedConfiguration(currentConfig, currentConfig), + false /* preserveWindow */, activityWindowInfo); + final ClientTransaction transaction = newTransaction(activity); + transaction.addTransactionItem(relaunchItem); + appThread.scheduleTransaction(transaction); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + verify(mActivityWindowInfoListener).accept(activityClientRecord.token, + activityWindowInfo); + } + + @Test + public void testActivityWindowInfoChanged_activityConfigurationChanged() + throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( + mActivityWindowInfoListener); + + final Activity activity = mActivityTestRule.launchActivity(new Intent()); + final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + clearInvocations(mActivityWindowInfoListener); + final Configuration config = new Configuration(activity.getResources().getConfiguration()); + config.seq++; + final Rect taskBounds = new Rect(0, 0, 1000, 2000); + final Rect taskFragmentBounds = new Rect(0, 0, 1000, 1000); + final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); + activityWindowInfo.set(true /* isEmbedded */, taskBounds, taskFragmentBounds); + final ActivityConfigurationChangeItem activityConfigurationChangeItem = + ActivityConfigurationChangeItem.obtain( + activity.getActivityToken(), config, activityWindowInfo); + final ClientTransaction transaction = newTransaction(activity); + transaction.addTransactionItem(activityConfigurationChangeItem); + appThread.scheduleTransaction(transaction); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + verify(mActivityWindowInfoListener).accept(activity.getActivityToken(), + activityWindowInfo); + + clearInvocations(mActivityWindowInfoListener); + final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo(); + activityWindowInfo2.set(true /* isEmbedded */, taskBounds, taskFragmentBounds); + config.seq++; + final ActivityConfigurationChangeItem activityConfigurationChangeItem2 = + ActivityConfigurationChangeItem.obtain( + activity.getActivityToken(), config, activityWindowInfo2); + final ClientTransaction transaction2 = newTransaction(activity); + transaction2.addTransactionItem(activityConfigurationChangeItem2); + appThread.scheduleTransaction(transaction); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + // The same ActivityWindowInfo won't trigger duplicated callback. + verify(mActivityWindowInfoListener, never()).accept(any(), any()); + } + /** * Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord, * Configuration, int, ActivityWindowInfo)} to try to push activity configuration to the @@ -871,7 +988,7 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newStopTransaction(@NonNull Activity activity) { final StopActivityItem stopStateRequest = StopActivityItem.obtain( - activity.getActivityToken(), 0 /* configChanges */); + activity.getActivityToken()); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(stopStateRequest); diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java index 4db5d1bf4f67..990739745f24 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java @@ -136,7 +136,7 @@ public class ClientTransactionItemTest { @Test public void testDestroyActivityItem_preExecute() { final DestroyActivityItem item = DestroyActivityItem - .obtain(mActivityToken, false /* finished */, 123 /* configChanges */); + .obtain(mActivityToken, false /* finished */); item.preExecute(mHandler); assertEquals(1, mActivitiesToBeDestroyed.size()); @@ -146,7 +146,7 @@ public class ClientTransactionItemTest { @Test public void testDestroyActivityItem_postExecute() { final DestroyActivityItem item = DestroyActivityItem - .obtain(mActivityToken, false /* finished */, 123 /* configChanges */); + .obtain(mActivityToken, false /* finished */); item.preExecute(mHandler); item.postExecute(mHandler, mPendingActions); @@ -156,11 +156,11 @@ public class ClientTransactionItemTest { @Test public void testDestroyActivityItem_execute() { final DestroyActivityItem item = DestroyActivityItem - .obtain(mActivityToken, false /* finished */, 123 /* configChanges */); + .obtain(mActivityToken, false /* finished */); item.execute(mHandler, mActivityClientRecord, mPendingActions); verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */, - eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any()); + eq(false) /* getNonConfigInstance */, any()); } @Test diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java index 213fd7bd494d..77d31a5f27e7 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java @@ -22,21 +22,29 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IDisplayManager; import android.os.Handler; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.view.DisplayInfo; +import android.window.ActivityWindowInfo; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.window.flags.Flags; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -44,6 +52,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.function.BiConsumer; + /** * Tests for {@link ClientTransactionListenerController}. * @@ -62,6 +72,10 @@ public class ClientTransactionListenerControllerTest { private IDisplayManager mIDisplayManager; @Mock private DisplayManager.DisplayListener mListener; + @Mock + private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener; + @Mock + private IBinder mActivityToken; private DisplayManagerGlobal mDisplayManager; private Handler mHandler; @@ -91,4 +105,24 @@ public class ClientTransactionListenerControllerTest { verify(mListener).onDisplayChanged(123); } + + @Test + public void testActivityWindowInfoChangedListener() { + mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + mController.registerActivityWindowInfoChangedListener(mActivityWindowInfoListener); + final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); + activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), + new Rect(0, 0, 1000, 1000)); + mController.onActivityWindowInfoChanged(mActivityToken, activityWindowInfo); + + verify(mActivityWindowInfoListener).accept(mActivityToken, activityWindowInfo); + + clearInvocations(mActivityWindowInfoListener); + mController.unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener); + + mController.onActivityWindowInfoChanged(mActivityToken, activityWindowInfo); + + verify(mActivityWindowInfoListener, never()).accept(any(), any()); + } } diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 31ea6759c710..584fe16d00ac 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -98,7 +98,7 @@ public class ObjectPoolTests { @Test public void testRecycleDestroyActivityItem() { - testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true, 117)); + testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true)); } @Test @@ -169,7 +169,7 @@ public class ObjectPoolTests { @Test public void testRecyclePauseActivityItemItem() { - testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, 5, true, true)); + testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, true, true)); } @Test @@ -185,7 +185,7 @@ public class ObjectPoolTests { @Test public void testRecycleStopItem() { - testRecycle(() -> StopActivityItem.obtain(mActivityToken, 4)); + testRecycle(() -> StopActivityItem.obtain(mActivityToken)); } @Test diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index adb6f2a23847..935bc7565986 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -306,7 +306,7 @@ public class TransactionExecutorTests { final IBinder token = mock(IBinder.class); final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */); destroyTransaction.addTransactionItem( - DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */)); + DestroyActivityItem.obtain(token, false /* finished */)); destroyTransaction.preExecute(mTransactionHandler); // The activity should be added to to-be-destroyed container. assertEquals(1, activitiesToBeDestroyed.size()); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 75347bf2c8de..d451fe58e8e4 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -155,8 +155,7 @@ public class TransactionParcelTests { @Test public void testDestroy() { - DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */, - 135 /* configChanges */); + DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -244,8 +243,7 @@ public class TransactionParcelTests { public void testPause() { // Write to parcel PauseActivityItem item = PauseActivityItem.obtain(mActivityToken, true /* finished */, - true /* userLeaving */, 135 /* configChanges */, true /* dontReport */, - true /* autoEnteringPip */); + true /* userLeaving */, true /* dontReport */, true /* autoEnteringPip */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -272,7 +270,7 @@ public class TransactionParcelTests { @Test public void testStop() { // Write to parcel - StopActivityItem item = StopActivityItem.obtain(mActivityToken, 14 /* configChanges */); + StopActivityItem item = StopActivityItem.obtain(mActivityToken); writeAndPrepareForReading(item); // Read from parcel and assert @@ -305,8 +303,7 @@ public class TransactionParcelTests { ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( mActivityToken, config(), new ActivityWindowInfo()); - StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken, - 78 /* configChanges */); + StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken); ClientTransaction transaction = ClientTransaction.obtain(null /* client */); transaction.addTransactionItem(callback1); @@ -351,8 +348,7 @@ public class TransactionParcelTests { mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); // Write to parcel - StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken, - 78 /* configChanges */); + StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken); ClientTransaction transaction = ClientTransaction.obtain(null /* client */); transaction.addTransactionItem(lifecycleRequest); diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index 4a9cb7180a3f..0c1e8793bfc9 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -18,34 +18,52 @@ package android.content.res; import android.annotation.NonNull; import android.app.ResourcesManager; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.LocaleList; import android.platform.test.annotations.Postsubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Display; import android.view.DisplayAdjustments; +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Postsubmit +@RunWith(AndroidJUnit4.class) public class ResourcesManagerTest extends TestCase { private static final int SECONDARY_DISPLAY_ID = 1; private static final String APP_ONE_RES_DIR = "app_one.apk"; private static final String APP_ONE_RES_SPLIT_DIR = "app_one_split.apk"; private static final String APP_TWO_RES_DIR = "app_two.apk"; private static final String LIB_RES_DIR = "lib.apk"; + private static final String TEST_LIB = "com.android.frameworks.coretests.bdr_helper_app1"; private ResourcesManager mResourcesManager; private Map<Integer, DisplayMetrics> mDisplayMetricsMap; + private PackageManager mPackageManager; - @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { super.setUp(); mDisplayMetricsMap = new HashMap<>(); @@ -93,8 +111,14 @@ public class ResourcesManagerTest extends TestCase { return mDisplayMetricsMap.get(displayId); } }; + + mPackageManager = InstrumentationRegistry.getContext().getPackageManager(); } + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Test @SmallTest public void testMultipleCallsWithIdenticalParametersCacheReference() { Resources resources = mResourcesManager.getResources( @@ -109,6 +133,7 @@ public class ResourcesManagerTest extends TestCase { assertSame(resources.getImpl(), newResources.getImpl()); } + @Test @SmallTest public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() { Resources resources = mResourcesManager.getResources( @@ -125,6 +150,7 @@ public class ResourcesManagerTest extends TestCase { assertNotSame(resources, newResources); } + @Test @SmallTest public void testAddingASplitCreatesANewImpl() { Resources resources1 = mResourcesManager.getResources( @@ -142,6 +168,7 @@ public class ResourcesManagerTest extends TestCase { assertNotSame(resources1.getImpl(), resources2.getImpl()); } + @Test @SmallTest public void testUpdateConfigurationUpdatesAllAssetManagers() { Resources resources1 = mResourcesManager.getResources( @@ -187,6 +214,7 @@ public class ResourcesManagerTest extends TestCase { assertEquals(expectedConfig, resources3.getConfiguration()); } + @Test @SmallTest public void testTwoActivitiesWithIdenticalParametersShareImpl() { Binder activity1 = new Binder(); @@ -208,6 +236,7 @@ public class ResourcesManagerTest extends TestCase { assertSame(resources1.getImpl(), resources2.getImpl()); } + @Test @SmallTest public void testThemesGetUpdatedWithNewImpl() { Binder activity1 = new Binder(); @@ -237,6 +266,7 @@ public class ResourcesManagerTest extends TestCase { assertTrue(value.data != 0); } + @Test @SmallTest public void testMultipleResourcesForOneActivityGetUpdatedWhenActivityBaseUpdates() { Binder activity1 = new Binder(); @@ -286,6 +316,7 @@ public class ResourcesManagerTest extends TestCase { assertEquals(expectedConfig2, resources2.getConfiguration()); } + @Test @SmallTest public void testChangingActivityDisplayDoesntOverrideDisplayRequestedByResources() { Binder activity = new Binder(); @@ -322,4 +353,101 @@ public class ResourcesManagerTest extends TestCase { assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels, defaultDisplayResources.getDisplayMetrics().widthPixels); } + + @Test + @SmallTest + @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS) + public void testExistingResourcesAfterResourcePathsRegistration() + throws PackageManager.NameNotFoundException { + // Inject ResourcesManager instance from this test to the ResourcesManager class so that all + // the static method can interact with this test smoothly. + ResourcesManager oriResourcesManager = ResourcesManager.getInstance(); + ResourcesManager.setInstance(mResourcesManager); + + // Create a Resources before register resources' paths for a package. + Resources resources = mResourcesManager.getResources( + null, APP_ONE_RES_DIR, null, null, null, null, null, null, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); + assertNotNull(resources); + ResourcesImpl oriResImpl = resources.getImpl(); + + ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0); + Resources.registerResourcePaths(TEST_LIB, appInfo); + + assertNotSame(oriResImpl, resources.getImpl()); + + String[] resourcePaths = appInfo.getAllApkPaths(); + resourcePaths = removeDuplicates(resourcePaths); + ApkAssets[] loadedAssets = resources.getAssets().getApkAssets(); + assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets)); + + // Package resources' paths should be cached in ResourcesManager. + assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance() + .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths())); + + // Revert the ResourcesManager instance back. + ResourcesManager.setInstance(oriResourcesManager); + } + + @Test + @SmallTest + @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS) + public void testNewResourcesAfterResourcePathsRegistration() + throws PackageManager.NameNotFoundException { + // Inject ResourcesManager instance from this test to the ResourcesManager class so that all + // the static method can interact with this test smoothly. + ResourcesManager oriResourcesManager = ResourcesManager.getInstance(); + ResourcesManager.setInstance(mResourcesManager); + + ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0); + Resources.registerResourcePaths(TEST_LIB, appInfo); + + // Create a Resources after register resources' paths for a package. + Resources resources = mResourcesManager.getResources( + null, APP_ONE_RES_DIR, null, null, null, null, null, null, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); + assertNotNull(resources); + + String[] resourcePaths = appInfo.getAllApkPaths(); + resourcePaths = removeDuplicates(resourcePaths); + ApkAssets[] loadedAssets = resources.getAssets().getApkAssets(); + assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets)); + + // Package resources' paths should be cached in ResourcesManager. + assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance() + .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths())); + + // Revert the ResourcesManager instance back. + ResourcesManager.setInstance(oriResourcesManager); + } + + private static boolean allResourcePathsLoaded(String[] resourcePaths, ApkAssets[] loadedAsset) { + for (int i = 0; i < resourcePaths.length; i++) { + if (!resourcePaths[i].endsWith(".apk")) { + continue; + } + boolean found = false; + for (int j = 0; j < loadedAsset.length; j++) { + if (loadedAsset[j].getAssetPath().equals(resourcePaths[i])) { + found = true; + } + } + if (!found) { + return false; + } + } + return true; + } + + private static String[] removeDuplicates(String[] paths) { + var pathList = new ArrayList<String>(); + var pathSet = new ArraySet<String>(); + final int pathsLen = paths.length; + for (int i = 0; i < pathsLen; i++) { + if (pathSet.add(paths[i])) { + pathList.add(paths[i]); + } + } + return pathList.toArray(new String[0]); + } } diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java index 4f722cefcf9f..6ab77dc9d535 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java @@ -355,7 +355,7 @@ public class RemoteViewsAdapterTest { } @Override - public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { + public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = new RemoteViews.RemoteCollectionItems.Builder(); itemsBuilder.setHasStableIds(hasStableIds()) diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java index 58cfc6625051..43e62275152e 100644 --- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java @@ -53,6 +53,7 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.test.InstrumentationRegistry; @@ -93,6 +94,9 @@ public class IntentForwarderActivityTest { private static final String TYPE_PLAIN_TEXT = "text/plain"; private static UserInfo MANAGED_PROFILE_INFO = new UserInfo(); + private static UserInfo PRIVATE_PROFILE_INFO = new UserInfo(12, "Private", null, + UserInfo.FLAG_PROFILE, UserManager.USER_TYPE_PROFILE_PRIVATE); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); static { MANAGED_PROFILE_INFO.id = 10; @@ -131,6 +135,7 @@ public class IntentForwarderActivityTest { @Before public void setup() { + MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getTargetContext(); sInjector = spy(new TestInjector()); @@ -632,6 +637,54 @@ public class IntentForwarderActivityTest { logMakerCaptor.getValue().getSubtype()); } + @Test + public void shouldForwardToParent_telephony_privateProfile() throws Exception { + mSetFlagsRule.enableFlags( + android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_INTENT_REDIRECTION); + + sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME; + when(mIPm.canForwardTo( + any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); + + List<UserInfo> profiles = new ArrayList<>(); + profiles.add(CURRENT_USER_INFO); + profiles.add(PRIVATE_PROFILE_INFO); + when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); + when(mUserManager.getProfileParent(anyInt())).thenReturn(CURRENT_USER_INFO); + Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); + intent.setAction(Intent.ACTION_DIAL); + IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); + verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt()); + assertEquals(activity.getStartActivityIntent().getAction(), intent.getAction()); + assertEquals(activity.getUserIdActivityLaunchedIn(), CURRENT_USER_INFO.id); + } + + @Test + public void shouldForwardToParent_mms_privateProfile() throws Exception { + mSetFlagsRule.enableFlags( + android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_INTENT_REDIRECTION); + + sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME; + when(mIPm.canForwardTo( + any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); + + List<UserInfo> profiles = new ArrayList<>(); + profiles.add(CURRENT_USER_INFO); + profiles.add(PRIVATE_PROFILE_INFO); + when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); + when(mUserManager.getProfileParent(anyInt())).thenReturn(CURRENT_USER_INFO); + Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); + intent.setAction(Intent.ACTION_SEND); + intent.setType(TYPE_PLAIN_TEXT); + IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); + verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt()); + assertEquals(activity.getStartActivityIntent().getAction(), intent.getAction()); + assertEquals(activity.getStartActivityIntent().getType(), intent.getType()); + assertEquals(activity.getUserIdActivityLaunchedIn(), CURRENT_USER_INFO.id); + } + private void setupShouldSkipDisclosureTest() throws RemoteException { sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME; sActivityName = "MyTestActivity"; @@ -688,6 +741,14 @@ public class IntentForwarderActivityTest { protected MetricsLogger getMetricsLogger() { return mMetricsLogger; } + + Intent getStartActivityIntent() { + return mStartActivityIntent; + } + + int getUserIdActivityLaunchedIn() { + return mUserIdActivityLaunchedIn; + } } public class TestInjector implements IntentForwarderActivity.Injector { diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 66be05ff233c..ed641e048a81 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -309,17 +309,17 @@ public class ActivityThreadClientTest { private void pauseActivity(ActivityClientRecord r) { mThread.handlePauseActivity(r, false /* finished */, - false /* userLeaving */, 0 /* configChanges */, false /* autoEnteringPip */, + false /* userLeaving */, false /* autoEnteringPip */, null /* pendingActions */, "test"); } private void stopActivity(ActivityClientRecord r) { - mThread.handleStopActivity(r, 0 /* configChanges */, + mThread.handleStopActivity(r, new PendingTransactionActions(), false /* finalStateRequest */, "test"); } private void destroyActivity(ActivityClientRecord r) { - mThread.handleDestroyActivity(r, true /* finishing */, 0 /* configChanges */, + mThread.handleDestroyActivity(r, true /* finishing */, false /* getNonConfigInstance */, "test"); } diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 238a3e10f058..1410950966e9 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -72,6 +72,12 @@ prebuilt_etc { src: "enhanced-confirmation.xml", } +prebuilt_etc { + name: "package-shareduid-allowlist.xml", + sub_dir: "sysconfig", + src: "package-shareduid-allowlist.xml", +} + // Privapp permission whitelist files prebuilt_etc { diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk index 783a7edadeb7..fd38d2782cb2 100644 --- a/data/etc/CleanSpec.mk +++ b/data/etc/CleanSpec.mk @@ -43,6 +43,8 @@ #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/sysconfig/package-shareduid-allowlist.xml) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/sysconfig/package-shareduid-allowlist.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.carrierconfig.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.carrierconfig.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.emergency.xml) diff --git a/data/etc/package-shareduid-allowlist.xml b/data/etc/package-shareduid-allowlist.xml new file mode 100644 index 000000000000..2401d4a26e68 --- /dev/null +++ b/data/etc/package-shareduid-allowlist.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> + +<!-- +This XML defines an allowlist for packages that want to join a particular shared-uid. +If a non-system package that is signed with platform signature, is trying to join a particular +shared-uid, and not in this list, the installation will fail. + +- The "package" XML attribute refers to the app's package name. +- The "shareduid" XML attribute refers to the shared uid name. + +Example usage + 1. <allow-package-shareduid package="com.example.app" shareduid="android.uid.system"/> + Indicates that a package - com.example.app, will be able to join android.uid.system. + 2. <allow-package-shareduid package="oem.example.app" shareduid="oem.uid.custom"/> + Indicates that a package - oem.example.app, will be able to join oem.uid.custom. +--> + +<config> + <allow-package-shareduid package="android.test.settings" shareduid="android.uid.system" /> +</config> diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml index 15ea15a9b4c1..53024ab858c4 100644 --- a/data/fonts/font_fallback.xml +++ b/data/fonts/font_fallback.xml @@ -792,14 +792,10 @@ </font> </family> <family lang="ja"> - <font weight="400" style="normal"> - NotoSerifHentaigana-EL.ttf + <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght"> + NotoSerifHentaigana.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="700" style="normal"> - NotoSerifHentaigana-EL.ttf - <axis tag="wght" stylevalue="700"/> - </font> </family> <family lang="ko"> <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular"> diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml index c1ca67ef456a..ac1b06495832 100644 --- a/data/fonts/font_fallback_cjkvf.xml +++ b/data/fonts/font_fallback_cjkvf.xml @@ -804,14 +804,10 @@ </font> </family> <family lang="ja"> - <font weight="400" style="normal"> - NotoSerifHentaigana-EL.ttf + <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght"> + NotoSerifHentaigana.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="700" style="normal"> - NotoSerifHentaigana-EL.ttf - <axis tag="wght" stylevalue="700"/> - </font> </family> <family lang="ko"> <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin" diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index b23f00554bdb..d1aa8e9734c2 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -1433,12 +1433,12 @@ </font> </family> <family lang="ja"> - <font weight="400" style="normal"> - NotoSerifHentaigana-EL.ttf + <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight"> + NotoSerifHentaigana.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="700" style="normal"> - NotoSerifHentaigana-EL.ttf + <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight"> + NotoSerifHentaigana.ttf <axis tag="wght" stylevalue="700"/> </font> </family> diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml index 1ab71ae270eb..9545ae718574 100644 --- a/data/fonts/fonts_cjkvf.xml +++ b/data/fonts/fonts_cjkvf.xml @@ -1532,12 +1532,12 @@ </font> </family> <family lang="ja"> - <font weight="400" style="normal"> - NotoSerifHentaigana-EL.ttf + <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight"> + NotoSerifHentaigana.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="700" style="normal"> - NotoSerifHentaigana-EL.ttf + <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight"> + NotoSerifHentaigana.ttf <axis tag="wght" stylevalue="700"/> </font> </family> diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java index 6da07198c3ad..884268a4b85c 100644 --- a/graphics/java/android/graphics/text/MeasuredText.java +++ b/graphics/java/android/graphics/text/MeasuredText.java @@ -29,11 +29,13 @@ import android.util.Log; import com.android.internal.util.Preconditions; import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.NeverInline; import libcore.util.NativeAllocationRegistry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Locale; import java.util.Objects; /** @@ -85,6 +87,30 @@ public class MeasuredText { return mChars; } + private void rangeCheck(int start, int end) { + if (start < 0 || start > end || end > mChars.length) { + throwRangeError(start, end); + } + } + + @NeverInline + private void throwRangeError(int start, int end) { + throw new IllegalArgumentException(String.format(Locale.US, + "start(%d) end(%d) length(%d) out of bounds", start, end, mChars.length)); + } + + private void offsetCheck(int offset) { + if (offset < 0 || offset >= mChars.length) { + throwOffsetError(offset); + } + } + + @NeverInline + private void throwOffsetError(int offset) { + throw new IllegalArgumentException(String.format(Locale.US, + "offset (%d) length(%d) out of bounds", offset, mChars.length)); + } + /** * Returns the width of a given range. * @@ -93,12 +119,7 @@ public class MeasuredText { */ public @FloatRange(from = 0.0) @Px float getWidth( @IntRange(from = 0) int start, @IntRange(from = 0) int end) { - Preconditions.checkArgument(0 <= start && start <= mChars.length, - "start(%d) must be 0 <= start <= %d", start, mChars.length); - Preconditions.checkArgument(0 <= end && end <= mChars.length, - "end(%d) must be 0 <= end <= %d", end, mChars.length); - Preconditions.checkArgument(start <= end, - "start(%d) is larger than end(%d)", start, end); + rangeCheck(start, end); return nGetWidth(mNativePtr, start, end); } @@ -120,12 +141,7 @@ public class MeasuredText { */ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull Rect rect) { - Preconditions.checkArgument(0 <= start && start <= mChars.length, - "start(%d) must be 0 <= start <= %d", start, mChars.length); - Preconditions.checkArgument(0 <= end && end <= mChars.length, - "end(%d) must be 0 <= end <= %d", end, mChars.length); - Preconditions.checkArgument(start <= end, - "start(%d) is larger than end(%d)", start, end); + rangeCheck(start, end); Preconditions.checkNotNull(rect); nGetBounds(mNativePtr, mChars, start, end, rect); } @@ -139,12 +155,7 @@ public class MeasuredText { */ public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull Paint.FontMetricsInt outMetrics) { - Preconditions.checkArgument(0 <= start && start <= mChars.length, - "start(%d) must be 0 <= start <= %d", start, mChars.length); - Preconditions.checkArgument(0 <= end && end <= mChars.length, - "end(%d) must be 0 <= end <= %d", end, mChars.length); - Preconditions.checkArgument(start <= end, - "start(%d) is larger than end(%d)", start, end); + rangeCheck(start, end); Objects.requireNonNull(outMetrics); long packed = nGetExtent(mNativePtr, mChars, start, end); @@ -160,8 +171,7 @@ public class MeasuredText { * @param offset an offset of the character. */ public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) { - Preconditions.checkArgument(0 <= offset && offset < mChars.length, - "offset(%d) is larger than text length %d" + offset, mChars.length); + offsetCheck(offset); return nGetCharWidthAt(mNativePtr, offset); } 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 038d0081ead8..1abda4287800 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -56,6 +56,7 @@ import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application; import android.app.Instrumentation; +import android.app.servertransaction.ClientTransactionListenerController; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -103,6 +104,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; +import java.util.function.BiConsumer; /** * Main controller class that manages split states and presentation. @@ -178,6 +180,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>(); + /** WM Jetpack set callback for {@link EmbeddedActivityWindowInfo}. */ + @GuardedBy("mLock") + @Nullable + private Pair<Executor, Consumer<EmbeddedActivityWindowInfo>> + mEmbeddedActivityWindowInfoCallback; + + /** Listener registered to {@link ClientTransactionListenerController}. */ + @GuardedBy("mLock") + @Nullable + private final BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener = + Flags.activityWindowInfoFlag() + ? this::onActivityWindowInfoChanged + : null; + private final Handler mHandler; final Object mLock = new Object(); private final ActivityStartMonitor mActivityStartMonitor; @@ -2456,6 +2472,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @VisibleForTesting + @Nullable + ActivityThread.ActivityClientRecord getActivityClientRecord(@NonNull Activity activity) { + return ActivityThread.currentActivityThread() + .getActivityClient(activity.getActivityToken()); + } + + @VisibleForTesting ActivityStartMonitor getActivityStartMonitor() { return mActivityStartMonitor; } @@ -2468,8 +2491,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @VisibleForTesting @Nullable IBinder getTaskFragmentTokenFromActivityClientRecord(@NonNull Activity activity) { - final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread() - .getActivityClient(activity.getActivityToken()); + final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity); return record != null ? record.mTaskFragmentToken : null; } @@ -2876,17 +2898,102 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + @Override + public void setEmbeddedActivityWindowInfoCallback(@NonNull Executor executor, + @NonNull Consumer<EmbeddedActivityWindowInfo> callback) { + if (!Flags.activityWindowInfoFlag()) { + return; + } + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + synchronized (mLock) { + if (mEmbeddedActivityWindowInfoCallback == null) { + ClientTransactionListenerController.getInstance() + .registerActivityWindowInfoChangedListener(getActivityWindowInfoListener()); + } + mEmbeddedActivityWindowInfoCallback = new Pair<>(executor, callback); + } + } + + @Override + public void clearEmbeddedActivityWindowInfoCallback() { + if (!Flags.activityWindowInfoFlag()) { + return; + } + synchronized (mLock) { + if (mEmbeddedActivityWindowInfoCallback == null) { + return; + } + mEmbeddedActivityWindowInfoCallback = null; + ClientTransactionListenerController.getInstance() + .unregisterActivityWindowInfoChangedListener(getActivityWindowInfoListener()); + } + } + + @VisibleForTesting + @GuardedBy("mLock") + @Nullable + BiConsumer<IBinder, ActivityWindowInfo> getActivityWindowInfoListener() { + return mActivityWindowInfoListener; + } + + @Nullable + @Override + public EmbeddedActivityWindowInfo getEmbeddedActivityWindowInfo(@NonNull Activity activity) { + if (!Flags.activityWindowInfoFlag()) { + return null; + } + synchronized (mLock) { + final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity); + return activityWindowInfo != null + ? translateActivityWindowInfo(activity, activityWindowInfo) + : null; + } + } + + @VisibleForTesting + void onActivityWindowInfoChanged(@NonNull IBinder activityToken, + @NonNull ActivityWindowInfo activityWindowInfo) { + synchronized (mLock) { + if (mEmbeddedActivityWindowInfoCallback == null) { + return; + } + final Executor executor = mEmbeddedActivityWindowInfoCallback.first; + final Consumer<EmbeddedActivityWindowInfo> callback = + mEmbeddedActivityWindowInfoCallback.second; + + final Activity activity = getActivity(activityToken); + if (activity == null) { + return; + } + final EmbeddedActivityWindowInfo info = translateActivityWindowInfo( + activity, activityWindowInfo); + + executor.execute(() -> callback.accept(info)); + } + } + @Nullable - private static ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) { + private ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) { if (activity.isFinishing()) { return null; } - final ActivityThread.ActivityClientRecord record = - ActivityThread.currentActivityThread() - .getActivityClient(activity.getActivityToken()); + final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity); return record != null ? record.getActivityWindowInfo() : null; } + @NonNull + private static EmbeddedActivityWindowInfo translateActivityWindowInfo( + @NonNull Activity activity, @NonNull ActivityWindowInfo activityWindowInfo) { + final boolean isEmbedded = activityWindowInfo.isEmbedded(); + final Rect activityBounds = new Rect(activity.getResources().getConfiguration() + .windowConfiguration.getBounds()); + final Rect taskBounds = new Rect(activityWindowInfo.getTaskBounds()); + final Rect activityStackBounds = new Rect(activityWindowInfo.getTaskFragmentBounds()); + return new EmbeddedActivityWindowInfo(activity, isEmbedded, activityBounds, taskBounds, + activityStackBounds); + } + /** * If the two rules have the same presentation, and the calculated {@link SplitAttributes} * matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same 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 00f8b5925d66..bdeeb7304b12 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 @@ -72,6 +72,8 @@ import static org.mockito.Mockito.times; import android.annotation.NonNull; import android.app.Activity; import android.app.ActivityOptions; +import android.app.ActivityThread; +import android.app.servertransaction.ClientTransactionListenerController; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -83,9 +85,11 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArraySet; import android.view.WindowInsets; import android.view.WindowMetrics; +import android.window.ActivityWindowInfo; import android.window.TaskFragmentInfo; import android.window.TaskFragmentOrganizer; import android.window.TaskFragmentParentInfo; @@ -99,7 +103,10 @@ import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.extensions.layout.WindowLayoutComponentImpl; import androidx.window.extensions.layout.WindowLayoutInfo; +import com.android.window.flags.Flags; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -110,6 +117,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -127,6 +136,9 @@ public class SplitControllerTest { private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent( new ComponentName("test", "placeholder")); + @Rule + public final SetFlagsRule mSetFlagRule = new SetFlagsRule(); + private Activity mActivity; @Mock private Resources mActivityResources; @@ -138,6 +150,13 @@ public class SplitControllerTest { private Handler mHandler; @Mock private WindowLayoutComponentImpl mWindowLayoutComponent; + @Mock + private ActivityWindowInfo mActivityWindowInfo; + @Mock + private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener; + @Mock + private androidx.window.extensions.core.util.function.Consumer<EmbeddedActivityWindowInfo> + mEmbeddedActivityWindowInfoCallback; private SplitController mSplitController; private SplitPresenter mSplitPresenter; @@ -1529,6 +1548,73 @@ public class SplitControllerTest { .getTopNonFinishingActivity(), secondaryActivity); } + @Test + public void testIsActivityEmbedded() { + mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + assertFalse(mSplitController.isActivityEmbedded(mActivity)); + + doReturn(true).when(mActivityWindowInfo).isEmbedded(); + + assertTrue(mSplitController.isActivityEmbedded(mActivity)); + } + + @Test + public void testGetEmbeddedActivityWindowInfo() { + mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + final boolean isEmbedded = true; + final Rect activityBounds = mActivity.getResources().getConfiguration().windowConfiguration + .getBounds(); + final Rect taskBounds = new Rect(0, 0, 1000, 2000); + final Rect activityStackBounds = new Rect(0, 0, 500, 2000); + doReturn(isEmbedded).when(mActivityWindowInfo).isEmbedded(); + doReturn(taskBounds).when(mActivityWindowInfo).getTaskBounds(); + doReturn(activityStackBounds).when(mActivityWindowInfo).getTaskFragmentBounds(); + + final EmbeddedActivityWindowInfo expected = new EmbeddedActivityWindowInfo(mActivity, + isEmbedded, activityBounds, taskBounds, activityStackBounds); + assertEquals(expected, mSplitController.getEmbeddedActivityWindowInfo(mActivity)); + } + + @Test + public void testSetEmbeddedActivityWindowInfoCallback() { + mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + final ClientTransactionListenerController controller = ClientTransactionListenerController + .getInstance(); + spyOn(controller); + doNothing().when(controller).registerActivityWindowInfoChangedListener(any()); + doReturn(mActivityWindowInfoListener).when(mSplitController) + .getActivityWindowInfoListener(); + final Executor executor = Runnable::run; + + // Register to ClientTransactionListenerController + mSplitController.setEmbeddedActivityWindowInfoCallback(executor, + mEmbeddedActivityWindowInfoCallback); + + verify(controller).registerActivityWindowInfoChangedListener(mActivityWindowInfoListener); + verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any()); + + // Test onActivityWindowInfoChanged triggered. + mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(), + mActivityWindowInfo); + + verify(mEmbeddedActivityWindowInfoCallback).accept(any()); + + // Unregister to ClientTransactionListenerController + mSplitController.clearEmbeddedActivityWindowInfoCallback(); + + verify(controller).unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener); + + // Test onActivityWindowInfoChanged triggered as no-op after clear callback. + clearInvocations(mEmbeddedActivityWindowInfoCallback); + mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(), + mActivityWindowInfo); + + verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any()); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); @@ -1537,13 +1623,17 @@ public class SplitControllerTest { /** Creates a mock activity in the organizer process. */ private Activity createMockActivity(int taskId) { final Activity activity = mock(Activity.class); + final ActivityThread.ActivityClientRecord activityClientRecord = + mock(ActivityThread.ActivityClientRecord.class); doReturn(mActivityResources).when(activity).getResources(); final IBinder activityToken = new Binder(); doReturn(activityToken).when(activity).getActivityToken(); doReturn(activity).when(mSplitController).getActivity(activityToken); + doReturn(activityClientRecord).when(mSplitController).getActivityClientRecord(activity); doReturn(taskId).when(activity).getTaskId(); doReturn(new ActivityInfo()).when(activity).getActivityInfo(); doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId(); + doReturn(mActivityWindowInfo).when(activityClientRecord).getActivityWindowInfo(); return activity; } diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml new file mode 100644 index 000000000000..ff49edb7a699 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FF000000" + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml index 490f0883fbfb..a5605a7ff50a 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml @@ -33,14 +33,15 @@ android:orientation="horizontal" android:clickable="true" android:focusable="true" - android:paddingStart="6dp" - android:paddingEnd="8dp"> + android:paddingStart="12dp"> <ImageView android:id="@+id/application_icon" android:layout_width="@dimen/desktop_mode_caption_icon_radius" android:layout_height="@dimen/desktop_mode_caption_icon_radius" android:layout_gravity="center_vertical" - android:contentDescription="@string/app_icon_text" /> + android:contentDescription="@string/app_icon_text" + android:layout_marginStart="6dp" + android:scaleType="centerCrop"/> <TextView android:id="@+id/application_name" @@ -53,8 +54,7 @@ android:lineHeight="20dp" android:layout_gravity="center_vertical" android:layout_weight="1" - android:paddingStart="8dp" - android:paddingEnd="8dp" + android:layout_marginStart="8dp" tools:text="Gmail"/> <ImageButton @@ -67,6 +67,7 @@ android:scaleType="fitCenter" android:clickable="false" android:focusable="false" + android:layout_marginHorizontal="8dp" android:layout_gravity="center_vertical"/> </LinearLayout> @@ -87,14 +88,15 @@ <ImageButton android:id="@+id/close_window" - android:layout_width="40dp" + android:layout_width="44dp" android:layout_height="40dp" - android:padding="4dp" + android:paddingHorizontal="10dp" + android:paddingVertical="8dp" android:layout_marginEnd="8dp" android:tint="?androidprv:attr/materialColorOnSurface" android:background="?android:selectableItemBackgroundBorderless" android:contentDescription="@string/close_button_text" - android:src="@drawable/decor_close_button_dark" - android:scaleType="fitCenter" + android:src="@drawable/desktop_mode_header_ic_close" + android:scaleType="centerCrop" android:gravity="end"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 8baaf2f155af..a541c590575f 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -145,4 +145,7 @@ <!-- Whether CompatUIController is enabled --> <bool name="config_enableCompatUIController">true</bool> + + <!-- Whether pointer pilfer is required to start back animation. --> + <bool name="config_backAnimationRequiresPointerPilfer">true</bool> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 48e6428524ae..7dd39613b438 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -434,15 +434,22 @@ <!-- (32 dp buttons + 10dp margins) * 3 buttons--> <dimen name="caption_right_buttons_width">126dp</dimen> - <!-- 2 buttons * 48dp button size. --> - <dimen name="desktop_mode_right_edge_buttons_width">96dp</dimen> + <!-- 2 buttons * 44dp button size + 16dp total margins. --> + <dimen name="desktop_mode_right_edge_buttons_width">104dp</dimen> <!-- 22dp padding + 24dp app icon + 16dp expand button. Text varies in size, we will calculate that width separately. --> <dimen name="desktop_mode_app_details_width_minus_text">62dp</dimen> - <!-- 22dp padding + 24dp app icon + 16dp expand button + 86dp text (max) --> - <dimen name="desktop_mode_app_details_max_width">148dp</dimen> + <!-- When custom headers are requested, this is the width of the left-aligned region that is + taken up by caption elements and extra margins. The customizable region starts at the + end of this area. --> + <dimen name="desktop_mode_customizable_caption_margin_start">84dp</dimen> + + <!-- When custom headers are requested, this is the width of the right-aligned region that is + taken up by caption elements and extra margins. The customizable region ends at the + start of this area. --> + <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen> <!-- The width of the maximize menu in desktop mode. --> <dimen name="desktop_mode_maximize_menu_width">287dp</dimen> @@ -490,7 +497,7 @@ <dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen> <!-- The radius of the caption menu icon. --> - <dimen name="desktop_mode_caption_icon_radius">28dp</dimen> + <dimen name="desktop_mode_caption_icon_radius">24dp</dimen> <!-- The radius of the caption menu shadow. --> <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen> @@ -503,10 +510,6 @@ split select if dragged until the touch input is within the range. --> <dimen name="desktop_mode_transition_area_width">32dp</dimen> - <!-- The height of the area at the top of the screen where a freeform task will transition to - fullscreen if dragged until the top bound of the task is within the area. --> - <dimen name="desktop_mode_transition_area_height">16dp</dimen> - <!-- The width of the area where a desktop task will transition to fullscreen. --> <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 2606fb661e80..9bd8531d33dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -64,6 +64,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; +import com.android.wm.shell.R; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; @@ -115,6 +116,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mShouldStartOnNextMoveEvent = false; private boolean mOnBackStartDispatched = false; private boolean mPointerPilfered = false; + private final boolean mRequirePointerPilfer; private final FlingAnimationUtils mFlingAnimationUtils; @@ -220,6 +222,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mActivityTaskManager = activityTaskManager; mContext = context; mContentResolver = contentResolver; + mRequirePointerPilfer = + context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer); mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; @@ -560,7 +564,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void tryDispatchOnBackStarted( IOnBackInvokedCallback callback, BackMotionEvent backEvent) { - if (mOnBackStartDispatched || callback == null || !mPointerPilfered) { + if (mOnBackStartDispatched + || callback == null + || (!mPointerPilfered && mRequirePointerPilfer)) { return; } dispatchOnBackStarted(callback, backEvent); @@ -1006,6 +1012,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted); pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress); pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent); + pw.println(prefix + " mPointerPilfered=" + mPointerPilfered); + pw.println(prefix + " mRequirePointerPilfer=" + mRequirePointerPilfer); pw.println(prefix + " mCurrentTracker state:"); mCurrentTracker.dump(pw, prefix + " "); pw.println(prefix + " mQueuedTracker state:"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS index 7237d2bde39f..37ccd15587e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS @@ -1,2 +1,4 @@ # WM shell sub-modules splitscreen owner chenghsiuchang@google.com +jeremysim@google.com +peanutbutter@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index 7091c4b7210a..fb0ed1587055 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -98,6 +98,7 @@ public class DesktopModeVisualIndicator { * Based on the coordinates of the current drag event, determine which indicator type we should * display, including no visible indicator. */ + @NonNull IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) { final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone. @@ -136,18 +137,18 @@ public class DesktopModeVisualIndicator { Region calculateFullscreenRegion(DisplayLayout layout, @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) { final Region region = new Region(); - int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); + int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM + ? 2 * layout.stableInsets().top + : mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); // A thin, short Rect at the top of the screen. if (windowingMode == WINDOWING_MODE_FREEFORM) { int fromFreeformWidth = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width); - int fromFreeformHeight = mContext.getResources().getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2), -captionHeight, (layout.width() / 2) + (fromFreeformWidth / 2), - fromFreeformHeight)); + transitionHeight)); } // A screen-wide, shorter Rect if the task is in fullscreen or split. if (windowingMode == WINDOWING_MODE_FULLSCREEN @@ -155,7 +156,7 @@ public class DesktopModeVisualIndicator { region.union(new Rect(0, -captionHeight, layout.width(), - edgeTransitionHeight)); + transitionHeight)); } return region; } 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 index b9d0342137c5..654409f4a637 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -62,7 +62,6 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.draganddrop.DragAndDropController @@ -141,7 +140,7 @@ class DesktopTasksController( private val transitionAreaHeight get() = context.resources.getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_area_height + com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height ) private val transitionAreaWidth @@ -417,23 +416,9 @@ class DesktopTasksController( splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_DESKTOP_MODE ) - getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo -> - wct.removeTask(otherTaskInfo.token) - } } } - private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? { - val remainingTaskPosition: Int = - if (splitScreenController.getSplitPosition(taskId) - == SPLIT_POSITION_BOTTOM_OR_RIGHT) { - SPLIT_POSITION_TOP_OR_LEFT - } else { - SPLIT_POSITION_BOTTOM_OR_RIGHT - } - return splitScreenController.getTaskInfo(remainingTaskPosition) - } - /** * The second part of the animated drag to desktop transition, called after * [startDragToDesktop]. @@ -580,30 +565,7 @@ class DesktopTasksController( * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to. */ fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) { - val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return - - val stableBounds = Rect() - displayLayout.getStableBounds(stableBounds) - - val destinationWidth = stableBounds.width() / 2 - val destinationBounds = when (position) { - SnapPosition.LEFT -> { - Rect( - stableBounds.left, - stableBounds.top, - stableBounds.left + destinationWidth, - stableBounds.bottom - ) - } - SnapPosition.RIGHT -> { - Rect( - stableBounds.right - destinationWidth, - stableBounds.top, - stableBounds.right, - stableBounds.bottom - ) - } - } + val destinationBounds = getSnapBounds(taskInfo, position) if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return @@ -624,8 +586,35 @@ class DesktopTasksController( outBounds.set(0, 0, desiredWidth, desiredHeight) // Center the task in screen bounds outBounds.offset( - screenBounds.centerX() - outBounds.centerX(), - screenBounds.centerY() - outBounds.centerY()) + screenBounds.centerX() - outBounds.centerX(), + screenBounds.centerY() - outBounds.centerY()) + } + + private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect() + + val stableBounds = Rect() + displayLayout.getStableBounds(stableBounds) + + val destinationWidth = stableBounds.width() / 2 + return when (position) { + SnapPosition.LEFT -> { + Rect( + stableBounds.left, + stableBounds.top, + stableBounds.left + destinationWidth, + stableBounds.bottom + ) + } + SnapPosition.RIGHT -> { + Rect( + stableBounds.right - destinationWidth, + stableBounds.top, + stableBounds.right, + stableBounds.bottom + ) + } + } } /** @@ -661,7 +650,7 @@ class DesktopTasksController( ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } } - private fun releaseVisualIndicator() { + fun releaseVisualIndicator() { val t = SurfaceControl.Transaction() visualIndicator?.releaseVisualIndicator(t) visualIndicator = null @@ -942,16 +931,13 @@ class DesktopTasksController( taskSurface: SurfaceControl, inputX: Float, taskTop: Float - ) { + ): DesktopModeVisualIndicator.IndicatorType { // If the visual indicator does not exist, create it. - if (visualIndicator == null) { - visualIndicator = DesktopModeVisualIndicator( - syncQueue, taskInfo, displayController, context, taskSurface, - rootTaskDisplayAreaOrganizer) - } - // Then, update the indicator type. - val indicator = visualIndicator ?: return - indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) + val indicator = visualIndicator ?: DesktopModeVisualIndicator( + syncQueue, taskInfo, displayController, context, taskSurface, + rootTaskDisplayAreaOrganizer) + if (visualIndicator == null) visualIndicator = indicator + return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) } /** @@ -971,20 +957,28 @@ class DesktopTasksController( if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return } - if (taskBounds.top <= transitionAreaHeight) { - moveToFullscreenWithAnimation(taskInfo, position) - return - } - if (inputCoordinate.x <= transitionAreaWidth) { - releaseVisualIndicator() - snapToHalfScreen(taskInfo, SnapPosition.LEFT) - return - } - if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() - ?.minus(transitionAreaWidth) ?: return)) { - releaseVisualIndicator() - snapToHalfScreen(taskInfo, SnapPosition.RIGHT) - return + + val indicator = visualIndicator ?: return + val indicatorType = indicator.updateIndicatorType( + PointF(inputCoordinate.x, taskBounds.top.toFloat()), + taskInfo.windowingMode + ) + when (indicatorType) { + DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { + moveToFullscreenWithAnimation(taskInfo, position) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { + releaseVisualIndicator() + snapToHalfScreen(taskInfo, SnapPosition.LEFT) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { + releaseVisualIndicator() + snapToHalfScreen(taskInfo, SnapPosition.RIGHT) + } + DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR, + DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> { + releaseVisualIndicator() + } } // A freeform drag-move ended, remove the indicator immediately. releaseVisualIndicator() @@ -997,14 +991,28 @@ class DesktopTasksController( * @param y height of drag, to be checked against status bar height. */ fun onDragPositioningEndThroughStatusBar( + inputCoordinates: PointF, taskInfo: RunningTaskInfo, freeformBounds: Rect ) { - finalizeDragToDesktop(taskInfo, freeformBounds) - } - - private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int { - return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0 + val indicator = visualIndicator ?: return + val indicatorType = indicator + .updateIndicatorType(inputCoordinates, taskInfo.windowingMode) + when (indicatorType) { + DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> { + finalizeDragToDesktop(taskInfo, freeformBounds) + } + DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR, + DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { + cancelDragToDesktop(taskInfo) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { + finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT)) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { + finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT)) + } + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 2cdec81d77ac..4d47ca998d8b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -122,6 +122,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private static final long PIP_KEEP_CLEAR_AREAS_DELAY = SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200); + private static final long ENABLE_TOUCH_DELAY_MS = 200L; + private Context mContext; protected ShellExecutor mMainExecutor; private DisplayController mDisplayController; @@ -152,6 +154,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback = this::onKeepClearAreasChangedCallback; + private final Runnable mEnableTouchCallback = () -> mTouchHandler.setTouchEnabled(true); + private void onKeepClearAreasChangedCallback() { if (mIsKeyguardShowingOrAnimating) { // early bail out if the change was caused by keyguard showing up @@ -1037,6 +1041,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb saveReentryState(pipBounds); } // Disable touches while the animation is running + mMainExecutor.removeCallbacks(mEnableTouchCallback); mTouchHandler.setTouchEnabled(false); if (mPinnedStackAnimationRecentsCallback != null) { mPinnedStackAnimationRecentsCallback.onPipAnimationStarted(); @@ -1067,7 +1072,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb InteractionJankMonitor.getInstance().end(CUJ_PIP_TRANSITION); // Re-enable touches after the animation completes - mTouchHandler.setTouchEnabled(true); + mMainExecutor.executeDelayed(mEnableTouchCallback, ENABLE_TOUCH_DELAY_MS); mTouchHandler.onPinnedStackAnimationEnded(direction); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS index 7237d2bde39f..37ccd15587e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS @@ -1,2 +1,4 @@ # WM shell sub-modules splitscreen owner chenghsiuchang@google.com +jeremysim@google.com +peanutbutter@google.com 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 2321869d2f4a..76504447339f 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 @@ -55,6 +55,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASO import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT; @@ -1607,6 +1608,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // The device is folded case EXIT_REASON_FULLSCREEN_SHORTCUT: // User has used a keyboard shortcut to go back to fullscreen from split + case EXIT_REASON_DESKTOP_MODE: + // One of the children enters desktop mode return true; default: return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 8d798a3035f6..f4ccd689f938 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -30,6 +30,10 @@ import static android.view.WindowInsets.Type.statusBars; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION; import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE; @@ -81,6 +85,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; @@ -119,9 +124,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; - private final Optional<DesktopTasksController> mDesktopTasksController; + private final DesktopTasksController mDesktopTasksController; private final InputManager mInputManager; - private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); @@ -129,8 +133,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final ExclusionRegionListener mExclusionRegionListener = new ExclusionRegionListenerImpl(); - private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId = - new SparseArray<>(); + private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId; private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); private final InputMonitorFactory mInputMonitorFactory; private TaskOperations mTaskOperations; @@ -197,7 +200,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, - rootTaskDisplayAreaOrganizer); + rootTaskDisplayAreaOrganizer, + new SparseArray<>()); } @VisibleForTesting @@ -219,7 +223,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -231,7 +236,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDisplayInsetsController = displayInsetsController; mSyncQueue = syncQueue; mTransitions = transitions; - mDesktopTasksController = desktopTasksController; + mDesktopTasksController = desktopTasksController.get(); mShellCommandHandler = shellCommandHandler; mWindowManager = windowManager; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; @@ -239,6 +244,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTransactionFactory = transactionFactory; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; mInputManager = mContext.getSystemService(InputManager.class); + mWindowDecorByTaskId = windowDecorByTaskId; shellInit.addInitCallback(this::onInit, this); } @@ -248,8 +254,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mShellCommandHandler.addDumpCallback(this::dump, this); mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), new DesktopModeOnInsetsChangedListener()); - mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener( - new DeskopModeOnTaskResizeAnimationListener())); + mDesktopTasksController.setOnTaskResizeAnimationListener( + new DeskopModeOnTaskResizeAnimationListener()); try { mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener, mContext.getDisplayId()); @@ -273,7 +279,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null && DesktopModeStatus.isEnabled() && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo)); + mDesktopTasksController.moveToSplit(decor.mTaskInfo); } } } @@ -340,8 +346,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void destroyWindowDecoration(RunningTaskInfo taskInfo) { - final DesktopModeWindowDecoration decoration = - mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); if (decoration == null) return; decoration.close(); @@ -349,6 +354,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (mEventReceiversByDisplay.contains(displayId)) { removeTaskFromEventReceiver(displayId); } + // Remove the decoration from the cache last because WindowDecoration#close could still + // issue CANCEL MotionEvents to touch listeners before its view host is released. + // See b/327664694. + mWindowDecorByTaskId.remove(taskInfo.taskId); } private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener @@ -414,14 +423,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeHandleMenu(); } } else if (id == R.id.desktop_button) { - if (mDesktopTasksController.isPresent()) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // App sometimes draws before the insets from WindowDecoration#relayout have - // been added, so they must be added here - mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); - mDesktopTasksController.get().moveToDesktop(mTaskId, wct); - closeOtherSplitTask(mTaskId); - } + final WindowContainerTransaction wct = new WindowContainerTransaction(); + // App sometimes draws before the insets from WindowDecoration#relayout have + // been added, so they must be added here + mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); + mDesktopTasksController.moveToDesktop(mTaskId, wct); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { decoration.closeHandleMenu(); @@ -429,42 +435,37 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSplitScreenController.moveTaskToFullscreen(mTaskId, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { - mDesktopTasksController.ifPresent(c -> - c.moveToFullscreen(mTaskId)); + mDesktopTasksController.moveToFullscreen(mTaskId); } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); - mDesktopTasksController.ifPresent(c -> { - c.requestSplit(decoration.mTaskInfo); - }); + mDesktopTasksController.requestSplit(decoration.mTaskInfo); } else if (id == R.id.collapse_menu_button) { decoration.closeHandleMenu(); } else if (id == R.id.select_button) { if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) { // TODO(b/278084491): dev option to enable display switching // remove when select is implemented - mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId)); + mDesktopTasksController.moveToNextDisplay(mTaskId); } } else if (id == R.id.maximize_window) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); - mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); + mDesktopTasksController.toggleDesktopTaskSize(taskInfo); } else if (id == R.id.maximize_menu_maximize_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; - mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); + mDesktopTasksController.toggleDesktopTaskSize(taskInfo); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_left_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; - mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( - taskInfo, SnapPosition.LEFT)); + mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.LEFT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_right_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; - mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( - taskInfo, SnapPosition.RIGHT)); + mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.RIGHT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } @@ -559,8 +560,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } else if (ev.getAction() == ACTION_HOVER_EXIT) { if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) { decoration.onMaximizeWindowHoverExit(); - } else if (id == R.id.maximize_window - || MaximizeMenu.Companion.isMaximizeMenuView(id)) { + } else if (id == R.id.maximize_window || id == R.id.maximize_menu) { // Close menu if not hovering over maximize menu or maximize button after a // delay to give user a chance to re-enter view or to move from one maximize // menu view to another. @@ -574,7 +574,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private void moveTaskToFront(RunningTaskInfo taskInfo) { if (!taskInfo.isFocused) { - mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo)); + mDesktopTasksController.moveTaskToFront(taskInfo); } } @@ -618,10 +618,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int dragPointerIdx = e.findPointerIndex(mDragPointerId); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); - mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo, + mDesktopTasksController.onDragPositioningMove(taskInfo, decoration.mTaskSurface, e.getRawX(dragPointerIdx), - newTaskBounds)); + newTaskBounds); mIsDragging = true; return true; } @@ -643,10 +643,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx))); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); - mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo, - position, + mDesktopTasksController.onDragPositioningEnd(taskInfo, position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), - newTaskBounds)); + newTaskBounds); if (touchingButton && !mHasLongClicked) { // We need the input event to not be consumed here to end the ripple // effect on the touched button. We will reset drag state in the ensuing @@ -674,10 +673,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && action != MotionEvent.ACTION_CANCEL)) { return false; } - mDesktopTasksController.ifPresent(c -> { - final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); - c.toggleDesktopTaskSize(decoration.mTaskInfo); - }); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo); return true; } } @@ -841,20 +838,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { + final DesktopModeVisualIndicator.IndicatorType indicatorType = + mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo, + relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); mTransitionDragActive = false; - final int statusBarHeight = getStatusBarHeight( - relevantDecor.mTaskInfo.displayId); - if (ev.getRawY() > 2 * statusBarHeight) { + if (indicatorType == TO_DESKTOP_INDICATOR + || indicatorType == TO_SPLIT_LEFT_INDICATOR + || indicatorType == TO_SPLIT_RIGHT_INDICATOR) { if (DesktopModeStatus.isEnabled()) { animateToDesktop(relevantDecor, ev); } mMoveToDesktopAnimator = null; return; } else if (mMoveToDesktopAnimator != null) { - mDesktopTasksController.ifPresent( - c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo)); + mDesktopTasksController.onDragPositioningEndThroughStatusBar( + new PointF(ev.getRawX(), ev.getRawY()), + relevantDecor.mTaskInfo, + calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE)); mMoveToDesktopAnimator = null; return; + } else { + // In cases where we create an indicator but do not start the + // move-to-desktop animation, we need to dismiss it. + mDesktopTasksController.releaseVisualIndicator(); } } relevantDecor.checkClickEvent(ev); @@ -866,20 +872,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { - mDesktopTasksController.ifPresent( - c -> c.updateVisualIndicator( + final DesktopModeVisualIndicator.IndicatorType indicatorType = + mDesktopTasksController.updateVisualIndicator( relevantDecor.mTaskInfo, - relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY())); - final int statusBarHeight = getStatusBarHeight( - relevantDecor.mTaskInfo.displayId); - if (ev.getRawY() > statusBarHeight) { + relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); + if (indicatorType != TO_FULLSCREEN_INDICATOR) { if (mMoveToDesktopAnimator == null) { mMoveToDesktopAnimator = new MoveToDesktopAnimator( mContext, mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo, relevantDecor.mTaskSurface); - mDesktopTasksController.ifPresent( - c -> c.startDragToDesktop(relevantDecor.mTaskInfo, - mMoveToDesktopAnimator)); + mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo, + mMoveToDesktopAnimator); } } if (mMoveToDesktopAnimator != null) { @@ -925,6 +928,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Animates a window to the center, grows to freeform size, and transitions to Desktop Mode. * @param relevantDecor the window decor of the task to be animated * @param ev the motion event that triggers the animation + * TODO(b/315527000): This animation needs to be adjusted to allow snap left/right cases. + * Currently fullscreen -> split snap still animates to center screen before readjusting. */ private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor, MotionEvent ev) { @@ -948,13 +953,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mDesktopTasksController.ifPresent( - c -> { - c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, - calculateFreeformBounds(ev.getDisplayId(), - DesktopTasksController - .DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); - }); + mDesktopTasksController.onDragPositioningEndThroughStatusBar( + new PointF(ev.getRawX(), ev.getRawY()), + relevantDecor.mTaskInfo, + calculateFreeformBounds(ev.getDisplayId(), + DesktopTasksController + .DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); } }); animator.start(); @@ -1084,7 +1088,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DragPositioningCallback dragPositioningCallback; final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.desktop_mode_transition_area_height); + R.dimen.desktop_mode_fullscreen_from_desktop_height); if (!DesktopModeStatus.isVeiledResizeEnabled()) { dragPositioningCallback = new FluidResizeTaskPositioner( mTaskOrganizer, mTransitions, windowDecoration, mDisplayController, @@ -1119,12 +1123,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return mSplitScreenController.getTaskInfo(remainingTaskPosition); } - private void closeOtherSplitTask(int taskId) { - if (isTaskInSplitScreen(taskId)) { - mTaskOperations.closeTask(getOtherSplitTask(taskId).token); - } - } - private boolean isTaskInSplitScreen(int taskId) { return mSplitScreenController != null && mSplitScreenController.isTaskInSplitScreen(taskId); @@ -1189,12 +1187,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void onExclusionRegionChanged(int taskId, Region region) { - mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region)); + mDesktopTasksController.onExclusionRegionChanged(taskId, region); } @Override public void onExclusionRegionDismissed(int taskId) { - mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId)); + mDesktopTasksController.removeExclusionRegionForTask(taskId); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 9e999ae52835..39803e2afd34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -318,28 +318,25 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode()); relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId); - // The "app controls" type caption bar should report the occluding elements as bounding - // rects to the insets system so that apps can draw in the empty space left in the center. - if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor) { - // The "app chip" section of the caption bar, it's aligned to the left and its width - // varies depending on the length of the app name, but we'll report its max width for - // now. - // TODO(b/316387515): consider reporting the true width after it's been laid out. + if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor + && TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { + // App is requesting to customize the caption bar. Allow input to fall through to the + // windows below so that the app can respond to input events on their custom content. + relayoutParams.mAllowCaptionInputFallthrough = true; + // Report occluding elements as bounding rects to the insets system so that apps can + // draw in the empty space in the center: + // First, the "app chip" section of the caption bar (+ some extra margins). final RelayoutParams.OccludingCaptionElement appChipElement = new RelayoutParams.OccludingCaptionElement(); - appChipElement.mWidthResId = R.dimen.desktop_mode_app_details_max_width; + appChipElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_start; appChipElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.START; relayoutParams.mOccludingCaptionElements.add(appChipElement); - // The "controls" section of the caption bar (maximize, close btns). These are aligned - // to the right of the caption bar and have a fixed width. - // TODO(b/316387515): add additional padding for an exclusive drag-move region. + // Then, the right-aligned section (drag space, maximize and close buttons). final RelayoutParams.OccludingCaptionElement controlsElement = new RelayoutParams.OccludingCaptionElement(); - controlsElement.mWidthResId = R.dimen.desktop_mode_right_edge_buttons_width; + controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end; controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END; relayoutParams.mOccludingCaptionElements.add(controlsElement); - relayoutParams.mAllowCaptionInputFallthrough = - TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo); } if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) { relayoutParams.mShadowRadiusId = taskInfo.isFocused diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt index b7dd01faa543..58bbb030da01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt @@ -12,6 +12,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView import androidx.core.content.withStyledAttributes +import androidx.core.view.isVisible import com.android.internal.R.attr.materialColorOnSecondaryContainer import com.android.internal.R.attr.materialColorOnSurface import com.android.internal.R.attr.materialColorSecondaryContainer @@ -76,6 +77,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( closeWindowButton.imageTintList = ColorStateList.valueOf(color) maximizeWindowButton.imageTintList = ColorStateList.valueOf(color) expandMenuButton.imageTintList = ColorStateList.valueOf(color) + appNameTextView.isVisible = !taskInfo.isTransparentCaptionBarAppearance appNameTextView.setTextColor(color) appIconImageView.imageAlpha = alpha maximizeWindowButton.imageAlpha = alpha diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt index f8ce4ee8e1ce..9703dce8bf53 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt @@ -56,18 +56,16 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { context, taskSurface, taskDisplayAreaOrganizer) whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width()) whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height()) + whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS) } @Test fun testFullscreenRegionCalculation() { val transitionHeight = context.resources.getDimensionPixelSize( - R.dimen.desktop_mode_transition_area_height) + R.dimen.desktop_mode_fullscreen_from_desktop_height) val fromFreeformWidth = mContext.resources.getDimensionPixelSize( R.dimen.desktop_mode_fullscreen_from_desktop_width ) - val fromFreeformHeight = mContext.resources.getDimensionPixelSize( - R.dimen.desktop_mode_fullscreen_from_desktop_height - ) var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT) assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight)) @@ -77,7 +75,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2, -50, DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2, - fromFreeformHeight)) + 2 * STABLE_INSETS.top)) testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT) assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight)) @@ -135,5 +133,12 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { private const val TRANSITION_AREA_WIDTH = 32 private const val CAPTION_HEIGHT = 50 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) + private const val NAVBAR_HEIGHT = 50 + private val STABLE_INSETS = Rect( + DISPLAY_BOUNDS.left, + DISPLAY_BOUNDS.top + CAPTION_HEIGHT, + DISPLAY_BOUNDS.right, + DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT + ) } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 917fd715f71f..9bb5482de715 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -29,6 +29,7 @@ import android.hardware.display.VirtualDisplay import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import android.util.SparseArray import android.view.Choreographer import android.view.Display.DEFAULT_DISPLAY import android.view.IWindowManager @@ -72,6 +73,8 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.whenever import java.util.Optional import java.util.function.Supplier +import org.mockito.Mockito +import org.mockito.kotlin.spy /** Tests of [DesktopModeWindowDecorViewModel] */ @@ -102,6 +105,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { private val transactionFactory = Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() } + private val windowDecorByTaskIdSpy = spy(SparseArray<DesktopModeWindowDecoration>()) private lateinit var shellInit: ShellInit private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener @@ -110,6 +114,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Before fun setUp() { shellInit = ShellInit(mockShellExecutor) + windowDecorByTaskIdSpy.clear() desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( mContext, mockShellExecutor, @@ -128,7 +133,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockDesktopModeWindowDecorFactory, mockInputMonitorFactory, transactionFactory, - mockRootTaskDisplayAreaOrganizer + mockRootTaskDisplayAreaOrganizer, + windowDecorByTaskIdSpy ) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -332,6 +338,19 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { verify(decoration, times(1)).relayout(task) } + @Test + fun testDestroyWindowDecoration_closesBeforeCleanup() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + val decoration = setUpMockDecorationForTask(task) + val inOrder = Mockito.inOrder(decoration, windowDecorByTaskIdSpy) + + onTaskOpening(task) + desktopModeWindowDecorViewModel.destroyWindowDecoration(task) + + inOrder.verify(decoration).close() + inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId) + } + private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { desktopModeWindowDecorViewModel.onTaskOpening( task, diff --git a/libs/hostgraphics/ADisplay.cpp b/libs/hostgraphics/ADisplay.cpp new file mode 100644 index 000000000000..9cc1f40184e3 --- /dev/null +++ b/libs/hostgraphics/ADisplay.cpp @@ -0,0 +1,159 @@ +/* + * Copyright 2024 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. + */ + +#include <apex/display.h> +#include <utils/Errors.h> + +namespace android::display::impl { + +/** + * Implementation of ADisplayConfig + */ +struct DisplayConfigImpl { + /** + * The width in pixels of the display configuration. + */ + int32_t width{1080}; + + /** + * The height in pixels of the display configuration. + */ + + int32_t height{1920}; + + /** + * The refresh rate of the display configuration, in frames per second. + */ + float fps{60.0}; + + /** + * The vsync offset at which surfaceflinger runs, in nanoseconds. + */ + int64_t sfOffset{0}; + + /** + * The vsync offset at which applications run, in nanoseconds. + */ + int64_t appOffset{0}; +}; + +// DisplayConfigImpl allocation is not managed through C++ memory apis, so +// preventing calling the destructor here. +static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value); + +/** + * Implementation of ADisplay + */ +struct DisplayImpl { + /** + * The type of the display, i.e. whether it is an internal or external + * display. + */ + ADisplayType type; + + /** + * The preferred WCG dataspace + */ + ADataSpace wcgDataspace; + + /** + * The preferred WCG pixel format + */ + AHardwareBuffer_Format wcgPixelFormat; + + /** + * The config for this display. + */ + DisplayConfigImpl config; +}; + +// DisplayImpl allocation is not managed through C++ memory apis, so +// preventing calling the destructor here. +static_assert(std::is_trivially_destructible<DisplayImpl>::value); + +} // namespace android::display::impl + +using namespace android; +using namespace android::display::impl; + +namespace android { + +int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { + // This is running on host, so there are no physical displays available. + // Create 1 fake display instead. + DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>( + malloc(sizeof(DisplayImpl*) + sizeof(DisplayImpl))); + DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + 1); + + displayData[0] = DisplayImpl{ADisplayType::DISPLAY_TYPE_INTERNAL, + ADataSpace::ADATASPACE_UNKNOWN, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + DisplayConfigImpl()}; + impls[0] = displayData; + *outDisplays = reinterpret_cast<ADisplay**>(impls); + return 1; +} + +void ADisplay_release(ADisplay** displays) { + if (displays == nullptr) { + return; + } + free(displays); +} + +float ADisplay_getMaxSupportedFps(ADisplay* display) { + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + return impl->config.fps; +} + +ADisplayType ADisplay_getDisplayType(ADisplay* display) { + return reinterpret_cast<DisplayImpl*>(display)->type; +} + +void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace, + AHardwareBuffer_Format* outPixelFormat) { + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + *outDataspace = impl->wcgDataspace; + *outPixelFormat = impl->wcgPixelFormat; +} + +int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + *outConfig = reinterpret_cast<ADisplayConfig*>(&impl->config); + return OK; +} + +int32_t ADisplayConfig_getWidth(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->width; +} + +int32_t ADisplayConfig_getHeight(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->height; +} + +float ADisplayConfig_getFps(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->fps; +} + +int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset; +} + +int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset; +} + +} // namespace android diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index f166fdeafa41..4407af68de99 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -22,6 +22,7 @@ cc_library_host_static { srcs: [ ":libui_host_common", + "ADisplay.cpp", "Fence.cpp", "HostBufferQueue.cpp", "PublicFormat.cpp", @@ -32,16 +33,21 @@ cc_library_host_static { // When frameworks/native/include will be removed from the list of automatic includes. // We will have to copy necessary headers with a pre-build step (generated headers). ".", - "frameworks/native/libs/nativebase/include", - "frameworks/native/libs/nativewindow/include", "frameworks/native/libs/arect/include", "frameworks/native/libs/ui/include_private", ], + + header_libs: [ + "libnativebase_headers", + "libnativedisplay_headers", + "libnativewindow_headers", + ], + export_include_dirs: ["."], target: { windows: { enabled: true, - } + }, }, } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 40239b8c2719..54f94f5c4b14 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -346,6 +346,7 @@ cc_defaults { "jni/android_util_PathParser.cpp", "jni/Bitmap.cpp", + "jni/BitmapRegionDecoder.cpp", "jni/BufferUtils.cpp", "jni/HardwareBufferHelpers.cpp", "jni/BitmapFactory.cpp", @@ -421,7 +422,6 @@ cc_defaults { "jni/android_graphics_TextureLayer.cpp", "jni/android_graphics_HardwareRenderer.cpp", "jni/android_graphics_HardwareBufferRenderer.cpp", - "jni/BitmapRegionDecoder.cpp", "jni/GIFMovie.cpp", "jni/GraphicsStatsService.cpp", "jni/Movie.cpp", @@ -559,8 +559,13 @@ cc_defaults { "AnimatorManager.cpp", "CanvasTransform.cpp", "DamageAccumulator.cpp", + "DeviceInfo.cpp", + "FrameInfo.cpp", + "FrameInfoVisualizer.cpp", + "FrameMetricsReporter.cpp", "Gainmap.cpp", "Interpolator.cpp", + "JankTracker.cpp", "LightingInfo.cpp", "Matrix.cpp", "Mesh.cpp", @@ -623,13 +628,8 @@ cc_defaults { "utils/NdkUtils.cpp", "AutoBackendTextureRelease.cpp", "DeferredLayerUpdater.cpp", - "DeviceInfo.cpp", - "FrameInfo.cpp", - "FrameInfoVisualizer.cpp", "HardwareBitmapUploader.cpp", "HWUIProperties.sysprop", - "JankTracker.cpp", - "FrameMetricsReporter.cpp", "Layer.cpp", "LayerUpdateQueue.cpp", "ProfileDataContainer.cpp", @@ -643,7 +643,10 @@ cc_defaults { cflags: ["-Wno-implicit-fallthrough"], }, host: { - header_libs: ["libnativebase_headers"], + header_libs: [ + "libnativebase_headers", + "libnativedisplay_headers", + ], local_include_dirs: ["platform/host"], @@ -655,7 +658,11 @@ cc_defaults { "platform/host/WebViewFunctorManager.cpp", ], - cflags: ["-Wno-unused-private-field"], + cflags: [ + "-DHWUI_NULL_GPU", + "-DNULL_GPU_MAX_TEXTURE_SIZE=4096", + "-Wno-unused-private-field", + ], }, }, } diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index 59f21694fb77..1e53fc2164c7 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -249,6 +249,7 @@ bool FrameInfoVisualizer::consumeProperties() { } void FrameInfoVisualizer::dumpData(int fd) { +#ifdef __ANDROID__ RETURN_IF_PROFILING_DISABLED(); // This method logs the last N frames (where N is <= mDataSize) since the @@ -268,6 +269,7 @@ void FrameInfoVisualizer::dumpData(int fd) { durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers), durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted)); } +#endif } } /* namespace uirenderer */ diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 4b0ddd2fa2ef..638a060bdb1c 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -17,10 +17,10 @@ #include "JankTracker.h" #include <cutils/ashmem.h> +#include <cutils/trace.h> #include <errno.h> #include <inttypes.h> #include <log/log.h> -#include <sys/mman.h> #include <algorithm> #include <cmath> @@ -278,7 +278,7 @@ void JankTracker::recomputeThresholds(int64_t frameBudget) REQUIRES(mDataMutex) void JankTracker::dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data) { - +#ifdef __ANDROID__ if (description) { switch (description->type) { case JankTrackerType::Generic: @@ -296,9 +296,11 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, } data->dump(fd); dprintf(fd, "\n"); +#endif } void JankTracker::dumpFrames(int fd) { +#ifdef __ANDROID__ dprintf(fd, "\n\n---PROFILEDATA---\n"); for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { dprintf(fd, "%s", FrameInfoNames[i]); @@ -315,6 +317,7 @@ void JankTracker::dumpFrames(int fd) { } } dprintf(fd, "\n---PROFILEDATA---\n\n"); +#endif } void JankTracker::reset() REQUIRES(mDataMutex) { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index e358b57f6fe1..b1ad8b2eb1b9 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,32 +16,29 @@ #pragma once -#ifdef __ANDROID__ // Layoutlib does not support device info -#include "DeviceInfo.h" -#endif // __ANDROID__ - -#include "Outline.h" -#include "Rect.h" -#include "RevealClip.h" -#include "effects/StretchEffect.h" -#include "utils/MathUtils.h" -#include "utils/PaintUtils.h" - #include <SkBlendMode.h> -#include <SkImageFilter.h> #include <SkCamera.h> #include <SkColor.h> +#include <SkImageFilter.h> #include <SkMatrix.h> #include <SkRegion.h> - #include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> #include <stddef.h> #include <utils/Log.h> + #include <algorithm> #include <ostream> #include <vector> +#include "DeviceInfo.h" +#include "Outline.h" +#include "Rect.h" +#include "RevealClip.h" +#include "effects/StretchEffect.h" +#include "utils/MathUtils.h" +#include "utils/PaintUtils.h" + class SkBitmap; class SkColorFilter; class SkPaint; @@ -546,13 +543,9 @@ public: } bool fitsOnLayer() const { -#ifdef __ANDROID__ // Layoutlib does not support device info const DeviceInfo* deviceInfo = DeviceInfo::get(); return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); -#else - return mPrimitiveFields.mWidth <= 4096 && mPrimitiveFields.mHeight <= 4096; -#endif } bool promotedToLayer() const { diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 536ff781badc..2ea4e3f21163 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -16,6 +16,7 @@ #include "VectorDrawable.h" +#include <gui/TraceUtils.h> #include <math.h> #include <string.h> #include <utils/Log.h> @@ -26,12 +27,7 @@ #include "SkSamplingOptions.h" #include "SkScalar.h" #include "hwui/Paint.h" - -#ifdef __ANDROID__ #include "renderthread/RenderThread.h" -#endif - -#include <gui/TraceUtils.h> #include "utils/Macros.h" #include "utils/VectorDrawableUtils.h" diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt index e2857f96cdf2..892eabf14191 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt @@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable import android.text.TextUtils import android.util.Log import androidx.activity.result.IntentSenderRequest +import androidx.credentials.PasswordCredential import androidx.credentials.PublicKeyCredential import androidx.credentials.provider.Action import androidx.credentials.provider.AuthenticationAction @@ -125,6 +126,7 @@ private fun getCredentialOptionInfoList( pendingIntent = credentialEntry.pendingIntent, fillInIntent = it.frameworkExtrasIntent, credentialType = CredentialType.PASSWORD, + rawCredentialType = PasswordCredential.TYPE_PASSWORD_CREDENTIAL, credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(), userName = credentialEntry.username.toString(), displayName = credentialEntry.displayName?.toString(), @@ -134,6 +136,9 @@ private fun getCredentialOptionInfoList( isAutoSelectable = credentialEntry.isAutoSelectAllowed && credentialEntry.isAutoSelectAllowedFromOption, entryGroupId = credentialEntry.entryGroupId.toString(), + isDefaultIconPreferredAsSingleProvider = + credentialEntry.isDefaultIconPreferredAsSingleProvider, + affiliatedDomain = credentialEntry.affiliatedDomain?.toString(), ) ) } @@ -147,6 +152,7 @@ private fun getCredentialOptionInfoList( pendingIntent = credentialEntry.pendingIntent, fillInIntent = it.frameworkExtrasIntent, credentialType = CredentialType.PASSKEY, + rawCredentialType = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(), userName = credentialEntry.username.toString(), displayName = credentialEntry.displayName?.toString(), @@ -158,6 +164,9 @@ private fun getCredentialOptionInfoList( isAutoSelectable = credentialEntry.isAutoSelectAllowed && credentialEntry.isAutoSelectAllowedFromOption, entryGroupId = credentialEntry.entryGroupId.toString(), + isDefaultIconPreferredAsSingleProvider = + credentialEntry.isDefaultIconPreferredAsSingleProvider, + affiliatedDomain = credentialEntry.affiliatedDomain?.toString(), ) ) } @@ -171,6 +180,7 @@ private fun getCredentialOptionInfoList( pendingIntent = credentialEntry.pendingIntent, fillInIntent = it.frameworkExtrasIntent, credentialType = CredentialType.UNKNOWN, + rawCredentialType = credentialEntry.type, credentialTypeDisplayName = credentialEntry.typeDisplayName?.toString().orEmpty(), userName = credentialEntry.title.toString(), @@ -181,6 +191,9 @@ private fun getCredentialOptionInfoList( isAutoSelectable = credentialEntry.isAutoSelectAllowed && credentialEntry.isAutoSelectAllowedFromOption, entryGroupId = credentialEntry.entryGroupId.toString(), + isDefaultIconPreferredAsSingleProvider = + credentialEntry.isDefaultIconPreferredAsSingleProvider, + affiliatedDomain = credentialEntry.affiliatedDomain?.toString(), ) ) } diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt index a5d4730967a3..a657e97de3cc 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt @@ -31,6 +31,11 @@ class CredentialEntryInfo( fillInIntent: Intent?, /** Type of this credential used for sorting. Not localized so must not be directly displayed. */ val credentialType: CredentialType, + /** + * String type value of this credential used for sorting. Not localized so must not be directly + * displayed. + */ + val rawCredentialType: String, /** Localized type value of this credential used for display purpose. */ val credentialTypeDisplayName: String, val providerDisplayName: String, @@ -42,6 +47,8 @@ class CredentialEntryInfo( val isAutoSelectable: Boolean, val entryGroupId: String, // Used for deduplication, and displayed as the grouping title // "For <value-of-entryGroupId>" on the more-option screen. + val isDefaultIconPreferredAsSingleProvider: Boolean, + val affiliatedDomain: String?, ) : EntryInfo( providerId, entryKey, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index ccf401da5a4c..6a1998a5e24e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -20,6 +20,7 @@ import android.content.ComponentName import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.credentials.GetCredentialRequest import android.credentials.selection.CreateCredentialProviderData import android.credentials.selection.DisabledProviderData import android.credentials.selection.Entry @@ -44,6 +45,9 @@ import androidx.credentials.CreateCredentialRequest import androidx.credentials.CreateCustomCredentialRequest import androidx.credentials.CreatePasswordRequest import androidx.credentials.CreatePublicKeyCredentialRequest +import androidx.credentials.PasswordCredential +import androidx.credentials.PriorityHints +import androidx.credentials.PublicKeyCredential import androidx.credentials.provider.CreateEntry import androidx.credentials.provider.RemoteEntry import org.json.JSONObject @@ -162,6 +166,25 @@ private fun getPackageInfo( /** Utility functions for converting CredentialManager data structures to or from UI formats. */ class GetFlowUtils { companion object { + fun extractTypePriorityMap(request: GetCredentialRequest): Map<String, Int> { + val typePriorityMap = mutableMapOf<String, Int>() + request.credentialOptions.forEach {option -> + // TODO(b/280085288) - use jetpack conversion method when exposed, rather than + // parsing from the raw Bundle + val priority = option.candidateQueryData.getInt( + "androidx.credentials.BUNDLE_KEY_TYPE_PRIORITY_VALUE", + when (option.type) { + PasswordCredential.TYPE_PASSWORD_CREDENTIAL -> + PriorityHints.PRIORITY_PASSWORD_OR_SIMILAR + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> 100 + else -> PriorityHints.PRIORITY_DEFAULT + } + ) + typePriorityMap[option.type] = priority + } + return typePriorityMap + } + // Returns the list (potentially empty) of enabled provider. fun toProviderList( providerDataList: List<GetCredentialProviderData>, @@ -193,6 +216,9 @@ class GetFlowUtils { null } } + + val typePriorityMap = extractTypePriorityMap(getCredentialRequest) + return com.android.credentialmanager.getflow.RequestDisplayInfo( appName = originName?.ifEmpty { null } ?: getAppLabel(context.packageManager, requestInfo.packageName) @@ -203,6 +229,7 @@ class GetFlowUtils { // exposed. "androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"), preferTopBrandingContent = preferTopBrandingContent, + typePriorityMap = typePriorityMap, ) } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 293e1112636e..4e1f4ee2e565 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -33,7 +33,6 @@ import android.graphics.drawable.Icon import android.os.Bundle import android.os.CancellationSignal import android.os.OutcomeReceiver -import android.provider.Settings import android.service.autofill.AutofillService import android.service.autofill.Dataset import android.service.autofill.Field @@ -140,7 +139,7 @@ class CredentialAutofillService : AutofillService() { override fun onResult(result: GetCandidateCredentialsResponse) { Log.i(TAG, "getCandidateCredentials onResult") val fillResponse = convertToFillResponse(result, request, - responseClientState) + responseClientState, GetFlowUtils.extractTypePriorityMap(getCredRequest)) if (fillResponse != null) { callback.onSuccess(fillResponse) } else { @@ -197,7 +196,8 @@ class CredentialAutofillService : AutofillService() { private fun convertToFillResponse( getCredResponse: GetCandidateCredentialsResponse, filLRequest: FillRequest, - responseClientState: Bundle + responseClientState: Bundle, + typePriorityMap: Map<String, Int>, ): FillResponse? { val candidateProviders = getCredResponse.candidateProviderDataList if (candidateProviders.isEmpty()) { @@ -213,7 +213,7 @@ class CredentialAutofillService : AutofillService() { autofillIdToProvidersMap.forEach { (autofillId, providers) -> validFillResponse = processProvidersForAutofillId( filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder, - getCredResponse.intent) + getCredResponse.intent, typePriorityMap) .or(validFillResponse) } if (!validFillResponse) { @@ -229,7 +229,8 @@ class CredentialAutofillService : AutofillService() { providerDataList: ArrayList<GetCredentialProviderData>, entryIconMap: Map<String, Icon>, fillResponseBuilder: FillResponse.Builder, - bottomSheetIntent: Intent + bottomSheetIntent: Intent, + typePriorityMap: Map<String, Int>, ): Boolean { val providerList = GetFlowUtils.toProviderList( providerDataList, @@ -237,7 +238,8 @@ class CredentialAutofillService : AutofillService() { if (providerList.isEmpty()) { return false } - val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList) + val providerDisplayInfo: ProviderDisplayInfo = + toProviderDisplayInfo(providerList, typePriorityMap) var totalEntryCount = providerDisplayInfo.sortedUserNameToCredentialEntryList.size val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt index 8ff17e0d333a..965ee860672e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt @@ -78,6 +78,8 @@ fun Entry( isLockedAuthEntry: Boolean = false, enforceOneLine: Boolean = false, onTextLayout: (TextLayoutResult) -> Unit = {}, + /** Get flow only, if present, where be drawn as a line above the headline. */ + affiliatedDomainText: String? = null, ) { val iconPadding = Modifier.wrapContentSize().padding( // Horizontal padding should be 16dp, but the suggestion chip itself @@ -102,6 +104,13 @@ fun Entry( ) { // Apply weight so that the trailing icon can always show. Column(modifier = Modifier.wrapContentHeight().fillMaxWidth().weight(1f)) { + if (!affiliatedDomainText.isNullOrBlank()) { + BodySmallText( + text = affiliatedDomainText, + enforceOneLine = enforceOneLine, + onTextLayout = onTextLayout, + ) + } SmallTitleText( text = entryHeadlineText, enforceOneLine = enforceOneLine, @@ -143,14 +152,14 @@ fun Entry( }, ) } - } else if (entrySecondLineText != null) { + } else if (!entrySecondLineText.isNullOrBlank()) { BodySmallText( text = entrySecondLineText, enforceOneLine = enforceOneLine, onTextLayout = onTextLayout, ) } - if (entryThirdLineText != null) { + if (!entryThirdLineText.isNullOrBlank()) { BodySmallText( text = entryThirdLineText, enforceOneLine = enforceOneLine, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt index 02afc547e3f8..7966a86903f3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt @@ -34,9 +34,9 @@ class RemoteViewsFactory { private const val passwordCharacterLength = 15 fun createDropdownPresentation( - context: Context, - icon: Icon, - credentialEntryInfo: CredentialEntryInfo + context: Context, + icon: Icon, + credentialEntryInfo: CredentialEntryInfo ): RemoteViews { var layoutId: Int = com.android.credentialmanager.R.layout .credman_dropdown_presentation_layout @@ -45,41 +45,37 @@ class RemoteViewsFactory { return remoteViews } setRemoteViewsPaddings(remoteViews, context, /* primaryTextBottomPadding=*/0) - if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) { - val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName - remoteViews.setTextViewText(android.R.id.text1, displayName) - val secondaryText = if (credentialEntryInfo.displayName != null) + val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName + remoteViews.setTextViewText(android.R.id.text1, displayName) + val secondaryText = + if (credentialEntryInfo.displayName != null + && (credentialEntryInfo.displayName != credentialEntryInfo.userName)) (credentialEntryInfo.userName + " " + bulletPoint + " " + credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName) else (credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName) - remoteViews.setTextViewText(android.R.id.text2, secondaryText) - } else { - remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) - remoteViews.setTextViewText(android.R.id.text2, - bulletPoint.repeat(passwordCharacterLength)) - } + remoteViews.setTextViewText(android.R.id.text2, secondaryText) val textColorPrimary = ContextCompat.getColor(context, - com.android.credentialmanager.R.color.text_primary) + com.android.credentialmanager.R.color.text_primary) remoteViews.setTextColor(android.R.id.text1, textColorPrimary) val textColorSecondary = ContextCompat.getColor(context, com.android .credentialmanager.R.color.text_secondary) remoteViews.setTextColor(android.R.id.text2, textColorSecondary) remoteViews.setImageViewIcon(android.R.id.icon1, icon); remoteViews.setBoolean( - android.R.id.icon1, setAdjustViewBoundsMethodName, true); + android.R.id.icon1, setAdjustViewBoundsMethodName, true); remoteViews.setInt( - android.R.id.icon1, - setMaxHeightMethodName, - context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_icon_size)); + android.R.id.icon1, + setMaxHeightMethodName, + context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_icon_size)); remoteViews.setContentDescription(android.R.id.icon1, credentialEntryInfo .providerDisplayName); val drawableId = - com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one + com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one remoteViews.setInt( - android.R.id.content, setBackgroundResourceMethodName, drawableId); + android.R.id.content, setBackgroundResourceMethodName, drawableId); return remoteViews } @@ -89,68 +85,68 @@ class RemoteViewsFactory { val remoteViews = RemoteViews(context.packageName, layoutId) setRemoteViewsPaddings(remoteViews, context) remoteViews.setTextViewText(android.R.id.text1, ContextCompat.getString(context, - com.android.credentialmanager - .R.string.dropdown_presentation_more_sign_in_options_text)) + com.android.credentialmanager + .R.string.dropdown_presentation_more_sign_in_options_text)) val textColorPrimary = ContextCompat.getColor(context, - com.android.credentialmanager.R.color.text_primary) + com.android.credentialmanager.R.color.text_primary) remoteViews.setTextColor(android.R.id.text1, textColorPrimary) val icon = Icon.createWithResource(context, com .android.credentialmanager.R.drawable.more_horiz_24px) icon.setTint(ContextCompat.getColor(context, - com.android.credentialmanager.R.color.sign_in_options_icon_color)) + com.android.credentialmanager.R.color.sign_in_options_icon_color)) remoteViews.setImageViewIcon(android.R.id.icon1, icon) remoteViews.setBoolean( - android.R.id.icon1, setAdjustViewBoundsMethodName, true); + android.R.id.icon1, setAdjustViewBoundsMethodName, true); remoteViews.setInt( - android.R.id.icon1, - setMaxHeightMethodName, - context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_icon_size)); + android.R.id.icon1, + setMaxHeightMethodName, + context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_icon_size)); val drawableId = - com.android.credentialmanager.R.drawable.more_options_list_item + com.android.credentialmanager.R.drawable.more_options_list_item remoteViews.setInt( - android.R.id.content, setBackgroundResourceMethodName, drawableId); + android.R.id.content, setBackgroundResourceMethodName, drawableId); return remoteViews } private fun setRemoteViewsPaddings( - remoteViews: RemoteViews, context: Context) { + remoteViews: RemoteViews, context: Context) { val bottomPadding = context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) + com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) setRemoteViewsPaddings(remoteViews, context, bottomPadding) } private fun setRemoteViewsPaddings( - remoteViews: RemoteViews, context: Context, primaryTextBottomPadding: Int) { + remoteViews: RemoteViews, context: Context, primaryTextBottomPadding: Int) { val leftPadding = context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_view_left_padding) + com.android.credentialmanager.R.dimen.autofill_view_left_padding) val iconToTextPadding = context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding) + com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding) val rightPadding = context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_view_right_padding) + com.android.credentialmanager.R.dimen.autofill_view_right_padding) val topPadding = context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_view_top_padding) + com.android.credentialmanager.R.dimen.autofill_view_top_padding) val bottomPadding = context.resources.getDimensionPixelSize( - com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) + com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) remoteViews.setViewPadding( - android.R.id.icon1, - leftPadding, - /* top=*/0, - /* right=*/0, - /* bottom=*/0) + android.R.id.icon1, + leftPadding, + /* top=*/0, + /* right=*/0, + /* bottom=*/0) remoteViews.setViewPadding( - android.R.id.text1, - iconToTextPadding, - /* top=*/topPadding, - /* right=*/rightPadding, - primaryTextBottomPadding) + android.R.id.text1, + iconToTextPadding, + /* top=*/topPadding, + /* right=*/rightPadding, + primaryTextBottomPadding) remoteViews.setViewPadding( - android.R.id.text2, - iconToTextPadding, - /* top=*/0, - /* right=*/rightPadding, - /* bottom=*/bottomPadding) + android.R.id.text2, + iconToTextPadding, + /* top=*/0, + /* right=*/rightPadding, + /* bottom=*/bottomPadding) } private fun isDarkMode(context: Context): Boolean { diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 660db7007224..bc0ea02d5b15 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -41,6 +41,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextLayoutResult @@ -375,6 +376,7 @@ fun PrimarySelectionCard( } internal const val MAX_ENTRY_FOR_PRIMARY_PAGE = 4 + /** Draws the primary credential selection page, used starting from android V. */ @Composable fun PrimarySelectionCardVImpl( @@ -399,6 +401,10 @@ fun PrimarySelectionCardVImpl( ) SheetContainerCard { val preferTopBrandingContent = requestDisplayInfo.preferTopBrandingContent + val singleProviderId = findSingleProviderIdForPrimaryPage( + primaryPageCredentialEntryList, + primaryPageLockedEntryList + ) if (preferTopBrandingContent != null) { item { HeadlineProviderIconAndName( @@ -409,10 +415,6 @@ fun PrimarySelectionCardVImpl( } else { // When only one provider's entries will be displayed on the primary page, display that // provider's icon + name up top. - val singleProviderId = findSingleProviderIdForPrimaryPage( - primaryPageCredentialEntryList, - primaryPageLockedEntryList - ) if (singleProviderId != null) { // First should always work but just to be safe. val providerInfo = providerInfoList.firstOrNull { it.id == singleProviderId } @@ -479,14 +481,17 @@ fun PrimarySelectionCardVImpl( CredentialContainerCard { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { primaryPageCredentialEntryList.forEach { + val entry = it.sortedCredentialEntryList.first() CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), + credentialEntryInfo = entry, onEntrySelected = onEntrySelected, enforceOneLine = true, onTextLayout = { showMoreForTruncatedEntry.value = it.hasVisualOverflow }, hasSingleEntry = hasSingleEntry, + shouldOverrideIcon = entry.isDefaultIconPreferredAsSingleProvider && + (singleProviderId != null), ) } primaryPageLockedEntryList.forEach { @@ -750,30 +755,47 @@ fun CredentialEntryRow( onTextLayout: (TextLayoutResult) -> Unit = {}, // Make optional since the secondary page doesn't care about this value. hasSingleEntry: Boolean? = null, + // For primary page only, if all display entries come from the same provider AND if that + // provider has opted in via isDefaultIconPreferredAsSingleProvider, then we override the + // display icon to the default icon for the given credential type. + shouldOverrideIcon: Boolean = false, ) { val (username, displayName) = if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) userAndDisplayNameForPasskey( credentialEntryInfo.userName, credentialEntryInfo.displayName ?: "") else Pair(credentialEntryInfo.userName, credentialEntryInfo.displayName) + + // For primary page, if + val overrideIcon: Painter? = + if (shouldOverrideIcon) { + when (credentialEntryInfo.credentialType) { + CredentialType.PASSKEY -> painterResource(R.drawable.ic_passkey_24) + CredentialType.PASSWORD -> painterResource(R.drawable.ic_password_24) + else -> painterResource(R.drawable.ic_other_sign_in_24) + } + } else null + Entry( onClick = { onEntrySelected(credentialEntryInfo) }, - iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(), + iconImageBitmap = + if (overrideIcon == null) credentialEntryInfo.icon?.toBitmap()?.asImageBitmap() else null, shouldApplyIconImageBitmapTint = credentialEntryInfo.shouldTintIcon, // Fall back to iconPainter if iconImageBitmap isn't available iconPainter = - if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24) + if (overrideIcon != null) overrideIcon + else if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24) else null, entryHeadlineText = username, - entrySecondLineText = + entrySecondLineText = displayName, + entryThirdLineText = (if (hasSingleEntry != null && hasSingleEntry) if (credentialEntryInfo.credentialType == CredentialType.PASSKEY || credentialEntryInfo.credentialType == CredentialType.PASSWORD) - listOf(displayName) + emptyList() // Still show the type display name for all non-password/passkey types since it won't be // mentioned in the bottom sheet heading. - else listOf(displayName, credentialEntryInfo.credentialTypeDisplayName) + else listOf(credentialEntryInfo.credentialTypeDisplayName) else listOf( - displayName, credentialEntryInfo.credentialTypeDisplayName, credentialEntryInfo.providerDisplayName )).filterNot(TextUtils::isEmpty).let { itemsToDisplay -> @@ -784,6 +806,7 @@ fun CredentialEntryRow( }, enforceOneLine = enforceOneLine, onTextLayout = onTextLayout, + affiliatedDomainText = credentialEntryInfo.affiliatedDomain, ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index e7f11a15a06c..ef4018833721 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -18,9 +18,9 @@ package com.android.credentialmanager.getflow import android.credentials.flags.Flags.selectorUiImprovementsEnabled import android.graphics.drawable.Drawable +import androidx.credentials.PriorityHints import com.android.credentialmanager.model.get.ProviderInfo import com.android.credentialmanager.model.EntryInfo -import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.AuthenticationEntryInfo import com.android.credentialmanager.model.get.CredentialEntryInfo import com.android.credentialmanager.model.get.RemoteEntryInfo @@ -30,7 +30,8 @@ data class GetCredentialUiState( val isRequestForAllOptions: Boolean, val providerInfoList: List<ProviderInfo>, val requestDisplayInfo: RequestDisplayInfo, - val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList), + val providerDisplayInfo: ProviderDisplayInfo = + toProviderDisplayInfo(providerInfoList, requestDisplayInfo.typePriorityMap), val currentScreenState: GetScreenState = toGetScreenState( providerDisplayInfo, isRequestForAllOptions), val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo), @@ -79,6 +80,8 @@ data class RequestDisplayInfo( val preferIdentityDocUi: Boolean, // A top level branding icon + display name preferred by the app. val preferTopBrandingContent: TopBrandingContent?, + // Map of credential type -> priority. + val typePriorityMap: Map<String, Int>, ) data class TopBrandingContent( @@ -119,7 +122,8 @@ enum class GetScreenState { * @hide */ fun toProviderDisplayInfo( - providerInfoList: List<ProviderInfo> + providerInfoList: List<ProviderInfo>, + typePriorityMap: Map<String, Int>, ): ProviderDisplayInfo { val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>() val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>() @@ -147,7 +151,7 @@ fun toProviderDisplayInfo( } // Compose sortedUserNameToCredentialEntryList - val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp() + val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp(typePriorityMap) // Sort per username userNameToCredentialEntryMap.values.forEach { it.sortWith(comparator) @@ -203,13 +207,21 @@ private fun toGetScreenState( else GetScreenState.PRIMARY_SELECTION } -internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<CredentialEntryInfo> { +internal class CredentialEntryInfoComparatorByTypeThenTimestamp( + val typePriorityMap: Map<String, Int>, +) : Comparator<CredentialEntryInfo> { override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int { // First prefer passkey type for its security benefits - if (p0.credentialType != p1.credentialType) { - if (CredentialType.PASSKEY == p0.credentialType) { + if (p0.rawCredentialType != p1.rawCredentialType) { + val p0Priority = typePriorityMap.getOrDefault( + p0.rawCredentialType, PriorityHints.PRIORITY_DEFAULT + ) + val p1Priority = typePriorityMap.getOrDefault( + p1.rawCredentialType, PriorityHints.PRIORITY_DEFAULT + ) + if (p0Priority < p1Priority) { return -1 - } else if (CredentialType.PASSKEY == p1.credentialType) { + } else if (p1Priority < p0Priority) { return 1 } } diff --git a/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt b/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt index d9ba36e1cdfc..28d83ee157b6 100644 --- a/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt +++ b/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt @@ -53,6 +53,7 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) { preferImmediatelyAvailableCredentials = false, preferIdentityDocUi = false, preferTopBrandingContent = null, + typePriorityMap = emptyMap(), ) } @@ -68,7 +69,7 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) { fun singleCredentialScreen_M3BottomSheetDisabled() { setFlagsRule.disableFlags(Flags.FLAG_SELECTOR_UI_IMPROVEMENTS_ENABLED) val providerInfoList = buildProviderInfoList() - val providerDisplayInfo = toProviderDisplayInfo(providerInfoList) + val providerDisplayInfo = toProviderDisplayInfo(providerInfoList, emptyMap()) val activeEntry = toActiveEntry(providerDisplayInfo) screenshotRule.screenshotTest("singleCredentialScreen") { ModalBottomSheet( @@ -96,7 +97,7 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) { fun singleCredentialScreen_M3BottomSheetEnabled() { setFlagsRule.enableFlags(Flags.FLAG_SELECTOR_UI_IMPROVEMENTS_ENABLED) val providerInfoList = buildProviderInfoList() - val providerDisplayInfo = toProviderDisplayInfo(providerInfoList) + val providerDisplayInfo = toProviderDisplayInfo(providerInfoList, emptyMap()) val activeEntry = toActiveEntry(providerDisplayInfo) screenshotRule.screenshotTest( "singleCredentialScreen_newM3BottomSheet", @@ -148,6 +149,9 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) { lastUsedTimeMillis = null, isAutoSelectable = false, entryGroupId = "username", + isDefaultIconPreferredAsSingleProvider = false, + rawCredentialType = "unknown-type", + affiliatedDomain = null, ) ), authenticationEntryList = emptyList(), diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml index 61aa4e65128a..3c37df68456c 100644 --- a/packages/PackageInstaller/res/values-el/strings.xml +++ b/packages/PackageInstaller/res/values-el/strings.xml @@ -117,7 +117,7 @@ <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Κάτι πήγε στραβά"</string> <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Παρουσιάστηκε κάποιο πρόβλημα κατά την επαναφορά αυτής της εφαρμογής"</string> <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Δεν επαρκεί ο αποθηκευτικός χώρος"</string> - <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Για να επαναφέρετε αυτή την εφαρμογή, μπορείτε να ελευθερώσετε χώρο στη συσκευή. Απαιτούμενος αποθηκευτικός χώρος: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Για να επαναφέρετε αυτή την εφαρμογή, μπορείτε να αποδεσμεύσετε χώρο στη συσκευή. Απαιτούμενος αποθηκευτικός χώρος: <xliff:g id="BYTES">%1$s</xliff:g>"</string> <string name="unarchive_action_required_title" msgid="4971245740162604619">"Απαιτούμενη ενέργεια"</string> <string name="unarchive_action_required_body" msgid="1679431572983989231">"Ακολουθήστε τα επόμενα βήματα για να επαναφέρετε αυτή την εφαρμογή"</string> <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Το <xliff:g id="INSTALLERNAME">%1$s</xliff:g> είναι απενεργοποιημένο"</string> diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java index 634e067a12d4..cf2f85ed5356 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java @@ -20,7 +20,6 @@ import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH; import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID; -import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -28,10 +27,10 @@ import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.Flags; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.res.AssetFileDescriptor; +import android.Manifest; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -201,7 +200,7 @@ public class InstallStaging extends Activity { params.setPermissionState(Manifest.permission.USE_FULL_SCREEN_INTENT, PackageInstaller.SessionParams.PERMISSION_STATE_DENIED); - if (pfd != null && Flags.readInstallInfo()) { + if (pfd != null) { try { final PackageInstaller.InstallInfo result = installer.readInstallInfo(pfd, debugPathName, 0); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index e95a8e63d644..45bfe5469172 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.Flags; import android.content.pm.InstallSourceInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; @@ -400,10 +399,7 @@ public class PackageInstallerActivity extends Activity { final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1 /* defaultValue */); final SessionInfo info = mInstaller.getSessionInfo(sessionId); - String resolvedPath = null; - if (info != null && Flags.getResolvedApkPath()) { - resolvedPath = info.getResolvedBaseApkPath(); - } + String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null; if (info == null || !info.isSealed() || resolvedPath == null) { Log.w(TAG, "Session " + sessionId + " in funky state; ignoring"); finish(); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt index 22caabdabbf0..aeabbd53d177 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt +++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt @@ -25,7 +25,6 @@ import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo -import android.content.pm.Flags import android.content.pm.PackageInfo import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller.SessionInfo @@ -363,7 +362,7 @@ class InstallRepository(private val context: Context) { params.setPermissionState( Manifest.permission.USE_FULL_SCREEN_INTENT, SessionParams.PERMISSION_STATE_DENIED ) - if (pfd != null && Flags.readInstallInfo()) { + if (pfd != null) { try { val installInfo = packageInstaller.readInstallInfo(pfd, debugPathName, 0) params.setAppPackageName(installInfo.packageName) @@ -426,8 +425,7 @@ class InstallRepository(private val context: Context) { if (PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action) { val info = packageInstaller.getSessionInfo(sessionId) - val resolvedPath = - if (Flags.getResolvedApkPath()) info?.resolvedBaseApkPath else null + val resolvedPath = info?.resolvedBaseApkPath if (info == null || !info.isSealed || resolvedPath == null) { Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring") return InstallAborted(ABORT_REASON_INTERNAL_ERROR) diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp new file mode 100644 index 000000000000..8770dfa013d0 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/Android.bp @@ -0,0 +1,24 @@ +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_app { + name: "SettingsLibDataStoreShell", + platform_apis: true, +} + +android_robolectric_test { + name: "SettingsLibDataStoreTest", + srcs: ["src/**/*"], + static_libs: [ + "SettingsLibDataStore", + "androidx.test.ext.junit", + "guava", + "mockito-robolectric-prebuilt", // mockito deps order matters! + "mockito-kotlin2", + ], + java_resource_dirs: ["config"], + instrumentation_for: "SettingsLibDataStoreShell", + coverage_libs: ["SettingsLibDataStore"], + upstream: true, +} diff --git a/packages/SettingsLib/DataStore/tests/AndroidManifest.xml b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml new file mode 100644 index 000000000000..ffc24e4330d1 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.datastore.test"> + + <application android:debuggable="true" /> +</manifest> diff --git a/packages/SettingsLib/DataStore/tests/config/robolectric.properties b/packages/SettingsLib/DataStore/tests/config/robolectric.properties new file mode 100644 index 000000000000..fab7251d020b --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/config/robolectric.properties @@ -0,0 +1 @@ +sdk=NEWEST_SDK diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt new file mode 100644 index 000000000000..bb791dc9a23c --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 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.settingslib.datastore + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors +import java.util.concurrent.Executor +import java.util.concurrent.atomic.AtomicInteger +import org.junit.Assert.assertThrows +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.reset +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class ObserverTest { + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var observer1: Observer + + @Mock private lateinit var observer2: Observer + + @Mock private lateinit var executor: Executor + + private val observable = DataObservable() + + @Test + fun addObserver_sameExecutor() { + observable.addObserver(observer1, executor) + observable.addObserver(observer1, executor) + } + + @Test + fun addObserver_differentExecutor() { + observable.addObserver(observer1, executor) + assertThrows(IllegalStateException::class.java) { + observable.addObserver(observer1, MoreExecutors.directExecutor()) + } + } + + @Test + fun addObserver_weaklyReferenced() { + val counter = AtomicInteger() + var observer: Observer? = Observer { counter.incrementAndGet() } + observable.addObserver(observer!!, MoreExecutors.directExecutor()) + + observable.notifyChange(ChangeReason.UPDATE) + assertThat(counter.get()).isEqualTo(1) + + // trigger GC, the observer callback should not be invoked + @Suppress("unused") + observer = null + System.gc() + System.runFinalization() + + observable.notifyChange(ChangeReason.UPDATE) + assertThat(counter.get()).isEqualTo(1) + } + + @Test + fun addObserver_notifyObservers_removeObserver() { + observable.addObserver(observer1, MoreExecutors.directExecutor()) + observable.addObserver(observer2, executor) + + observable.notifyChange(ChangeReason.DELETE) + + verify(observer1).onChanged(ChangeReason.DELETE) + verify(observer2, never()).onChanged(any()) + verify(executor).execute(any()) + + reset(observer1, executor) + observable.removeObserver(observer2) + + observable.notifyChange(ChangeReason.UPDATE) + verify(observer1).onChanged(ChangeReason.UPDATE) + verify(executor, never()).execute(any()) + } + + @Test + fun notifyChange_addObserverWithinCallback() { + // ConcurrentModificationException is raised if it is not implemented correctly + observable.addObserver( + { observable.addObserver(observer1, executor) }, + MoreExecutors.directExecutor() + ) + observable.notifyChange(ChangeReason.UPDATE) + } +} diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 7245099c4ff2..cc23f6eedf9a 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses is onderbreek om battery te beskerm"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Gaan die laaibykomstigheid na"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor gegrond op jou gebruik"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index dc91df021347..0ecd376626c4 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪን ለመጠበቅ ኃይል መሙላት በይቆይ ላይ"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - የኃይል መሙላት መለዋወጫን ይፈትሹ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ገደማ ቀርቷል"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"በአጠቃቀምዎ መሠረት <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 87c4d7fb99a7..1f313aef239c 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - الشحن معلَّق لحماية البطارية"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - فحص ملحق الشحن"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا."</string> <string name="power_discharging_duration" msgid="1076561255466053220">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا، بناءً على استخدامك"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index bcf9f5d25980..a4be8e997311 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সুৰক্ষিত কৰিবলৈ চাৰ্জিং স্থগিত ৰখা হৈছে"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিঙৰ সৈতে জড়িত আনুষংগিক সামগ্ৰী পৰীক্ষা কৰক"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্ৰায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 22dbff2036bb..df6dad208bd8 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyanı qorumaq üçün şarj gözlədilir"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarını yoxlayın"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"İstifadəyə əsasən təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index ee58e8ef5e04..8bb8c838ed39 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju da bi se zaštitila baterija"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Proverite dodatnu opremu za punjenje"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na osnovu korišćenja"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 079a8f3aaa11..433c2430a40d 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарадка прыпынена, каб абараніць акумулятар"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>, праверце зарадную прыладу"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Зараду хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Зараду (<xliff:g id="LEVEL">%2$s</xliff:g>) хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Зараду пры такім выкарыстанні хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 5a0c0b779f55..27e27bef4f3a 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е поставено на пауза с цел запазване на батерията"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Проверете аксесоара за зареждане"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g> въз основа на използването"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 6fb6fb3dc6cd..2cf055654f3b 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারিকে সুরক্ষিত রাখতে চার্জিং হোল্ড করা হয়েছে"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অ্যাক্সেসরি চেক করুন"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ব্যবহারের উপর ভিত্তি করে আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index a430848e79f3..64ccf98c3aa8 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju radi zaštite baterije"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjerite opremu za punjenje"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na osnovu vaše potrošnje"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 0bbb97960a76..81f9e97a2203 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: la càrrega s\'ha posat en espera per protegir la bateria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: revisa l\'accessori de càrrega"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 97d0d15bfedf..cb8c36b55580 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení je pozastaveno za účelem ochrany baterie"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Zkontrolujte nabíjecí příslušenství"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Při vašem obvyklém využití zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 096042246e01..625f05b57ac8 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Opladningen er sat på pause for at beskytte batteriet"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tjek opladningstilbehøret"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage, alt efter hvordan du bruger enheden"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index cb36cca64ff4..f622f61505f5 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladevorgang zum Schutz des Akkus angehalten"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladezubehör prüfen"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 4953884b0d74..861f4722edfb 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Η φόρτιση τέθηκε σε αναμονή για προστασία της μπαταρίας"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Έλεγχος αξεσουάρ φόρτισης"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, βάσει της χρήσης σας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index d730fb7951e1..e6923de1af8c 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – check charging accessory"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index d730fb7951e1..e6923de1af8c 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – check charging accessory"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index d730fb7951e1..e6923de1af8c 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – check charging accessory"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 775f23c14eac..a6fdf569954c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Se detuvo la carga para proteger la batería"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verifica el accesorio de carga"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g> en función de tu uso"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 70b85029a2b0..d57a33a305c6 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga pausada para proteger la batería"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Comprueba el accesorio de carga"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tiempo restante aproximado según tu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index daae2147ce7b..dfe78d8f3557 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on aku kaitsmiseks ootele pandud"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – kontrollige laadimistarvikut"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Teie kasutuse põhjal on jäänud ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 2fd01f3ab848..3c6309dad77f 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: kargatze-prozesua zain dago bateria babesteko"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Eman begiratu bat kargatzeko osagarriari"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Erabilera kontuan izanda, <xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 9a1e1076eae9..2cb1873e5fbe 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - برای محافظت از باتری، شارژ موقتاً متوقف شده است"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - لوازم شارژ را بررسی کنید"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"براساس مصرفتان، تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 0faa32c54442..29459ca5ee1f 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus on keskeytetty akun suojaamiseksi"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tarkista latauslisävaruste"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä käyttösi perusteella"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 24de4cd07a80..f97b36ea895d 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -257,7 +257,7 @@ <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Association de l\'appareil en cours…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association de l\'appareil Soit le code QR est incorrect, soit l\'appareil n\'est pas connecté au même réseau."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresse IP et port"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Numériser le code QR"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Balayer le code QR"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associer l\'appareil par Wi-Fi en numérisant un code QR"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Veuillez vous connecter à un réseau Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, développeur"</string> @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – La recharge a été mise en pause pour protéger la pile"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Vérifier l\'accessoire de recharge"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> en fonction de votre usage"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 8f61c8d604fa..7f7ba637b47d 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge en pause pour protéger la batterie"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> : vérifiez l\'accessoire de recharge"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 4cd9e2ca86b7..179d946c23da 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>. A carga púxose en pausa para protexer a batería"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>. Comproba o accesorio de carga"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado (<xliff:g id="LEVEL">%2$s</xliff:g>): <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado en función do uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 15f92875e22d..cafe86c26b77 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીને સુરક્ષિત રાખવા માટે, ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું છે"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઍક્સેસરી ચેક કરો"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 5c94489f8f77..53cf7247b996 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी को सुरक्षित रखने के लिए, फ़ोन को चार्ज होने से रोक दिया गया है"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऐक्सेसरी की जांच करें"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"आपके इस्तेमाल के हिसाब से बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index cc7b327e9d4f..f06baad6ea6c 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je pauzirano radi zaštite baterije"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjerite dodatak za punjenje"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na temelju vaše upotrebe"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index e9fd9dbd54e9..39d905f0bf46 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Az akkumulátor védelme érdekében a töltés szünetel"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ellenőrizze a töltőtartozékot"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra az eszköz használata alapján"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index d6b356005ac6..55f09e297f2a 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ստուգեք լիցքավորիչը"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 398853aa5b02..885729da1b66 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dihentikan sementara untuk melindungi baterai"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Periksa aksesori pengisian daya"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi berdasarkan penggunaan Anda"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 2be92e5dff33..020191b55e7c 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla í bið til að vernda rafhlöðuna"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Athugaðu hleðslubúnaðinn"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir miðað við notkun þína"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 3dc69e8ace46..37a20675abe7 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica in sospeso per proteggere la batteria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Controlla l\'accessorio di ricarica"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 9ef9cf1bc043..d87d0f8dafbc 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - バッテリーを保護するため、充電を一時停止しています"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電用アクセサリを確認してください"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(使用状況に基づく)"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 4ddb0054c329..7812f0b21601 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – დატენვა შეჩერებულია ბატარეის დასაცავად"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: მიმდინარეობს დამტენი აქსესუარის შემოწმება"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, ბატარეის მოხმარების გათვალისწინებით"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index fa46e8e93db6..300d4e517013 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: батареяны қорғау үшін зарядтау кідіртіледі."</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау құрылғысын тексеріңіз"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Пайдалану деректеріңізге сәйкес енді шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index cd2a6c41ef5a..373cfc656789 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"បដិសេធដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងផ្អាកការសាកថ្ម ដើម្បីការពារថ្ម"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ពិនិត្យមើលគ្រឿងសាកថ្ម"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"នៅសល់ប្រហែល <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ទៀត"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"នៅសល់ប្រហែល <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ទៀត (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"នៅសល់ប្រហែល <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ទៀត ផ្អែកលើការប្រើប្រាស់របស់អ្នក"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index a01a4bd1c37d..bf84cc48aa39 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯನ್ನು ರಕ್ಷಿಸಲು ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್ ಮಾಡಲಾಗಿದೆ"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಆ್ಯಕ್ಸೆಸರಿಯನ್ನು ಪರಿಶೀಲಿಸಿ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"(<xliff:g id="LEVEL">%2$s</xliff:g>) ತಲುಪಲು <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ನಿಮ್ಮ ಬಳಕೆಯ ಆಧಾರದ ಮೇಲೆ ಸುಮಾರು <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index c9c92e5894f7..b184ef43e5b7 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>, <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 배터리 보호를 위해 충전 일시중지"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 액세서리 확인"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"남은 시간: 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"남은 시간 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"내 사용량을 기준으로 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> 남음"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 44b01472b232..e565e444536c 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны коргоо үчүн кубаттоо тындырылды"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубаттоо шайманын текшериңиз"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 70e9b68551c3..6caaf952b364 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຢຸດການສາກຊົ່ວຄາວເພື່ອປົກປ້ອງແບັດເຕີຣີ"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກວດສອບອຸປະກອນເສີມສຳລັບການສາກ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ອ້າງອີງຈາກການນຳໃຊ້ຂອງທ່ານ"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index d50634c1ead1..70bcc2100ddd 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - полнењето е паузирано за да се заштити батеријата"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Проверете го додатокот за полнење"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> според вашето користење"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 085b7c16180c..9a297e5fb4d8 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി പരിരക്ഷിക്കാൻ ചാർജിംഗ് ഹോൾഡിലാണ്"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ആക്സസറി പരിശോധിക്കുക"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 60a31e8e3c3b..000e306afef3 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг хамгаалахын тулд цэнэглэхийг хүлээлгэсэн"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх нэмэлт хэрэгслийг шалгах"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Таны хэрэглээнд үндэслэн ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 32de0e5f5daa..6af0fbdc0ac8 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरीचे संरक्षण करण्यासाठी चार्जिंग थांबवले आहे"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंगसंबंधित ॲक्सेसरी तपासा"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"तुमच्या वापरावर आधारित अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index f0abd94dd9d1..621b4694026b 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan ditunda untuk melindungi bateri"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Periksa aksesori pengecasan"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi berdasarkan penggunaan anda"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 3d942855e00e..60161c344ebb 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီကာကွယ်ရန် အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းပစ္စည်း စစ်ရန်"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"သင်၏ အသုံးပြုမှု အပေါ် မူတည်၍ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index d286da13d8f1..256a71bb57c4 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er satt på vent for å beskytte batteriet"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Sjekk ladetilbehøret"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen basert på bruken din"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 146418c83e69..fdd965a6e52a 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ब्याट्री जोगाउन चार्जिङ होल्ड गरिएको छ"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिङ एक्सेसरी जाँच्नुहोस्"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"तपाईंको प्रयोगको आधारमा लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index f5ae2540d7ae..72e57af602e8 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: opladen is in de wacht gezet om de batterij te beschermen"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Oplaadaccessoire checken"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Nog ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Nog ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Nog ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> op basis van je gebruik"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index b71cf0ebd125..723af105f8ea 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍ରାଇଡ୍ କରାଯାଇଛି"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂ ଆକସେସୋରୀକୁ ଯାଞ୍ଚ କରନ୍ତୁ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ପାଇଁ (<xliff:g id="LEVEL">%2$s</xliff:g>) ବଳକା ଅଛି"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ଆପଣଙ୍କ ବ୍ୟବହାରକୁ ଆଧାର କରି ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 76d49214761d..8a77c12f6c0c 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਐਕਸੈਸਰੀ ਦੀ ਜਾਂਚ ਕਰੋ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 1b76b6f0fd03..375a35f6edef 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – wstrzymano ładowanie, aby chronić baterię"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – sprawdź akcesoria do ładowania"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (na podstawie Twojego sposobu korzystania)"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index bdfff4aa93c7..55b9db509200 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento suspenso para proteger a bateria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: verifique o acessório de carregamento"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado, com base no seu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 6fb851b7a32e..ca704e7bbb87 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento em espera para proteger a bateria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verificar acessório de carregamento"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> com base na sua utilização"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index bdfff4aa93c7..55b9db509200 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento suspenso para proteger a bateria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: verifique o acessório de carregamento"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado, com base no seu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index e8d68520b5c4..fa89c1a4c3d8 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcarea s-a întrerupt pentru a proteja bateria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Verifică accesoriul de încărcare"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"În baza utilizării, timpul rămas este de aproximativ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index ef1e817614d7..d85307098a35 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"Уровень заряда – <xliff:g id="PERCENTAGE">%1$s</xliff:g>. <xliff:g id="TIME_STRING">%2$s</xliff:g>."</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>, зарядка приостановлена для защиты батареи"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>, проверьте зарядное устройство"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g> при текущем уровне расхода"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index cf83ee8cc27f..093f216afccc 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය රඳවා තබා ඇත"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණ ආයිත්තම පරීක්ෂා කරන්න"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ක් පමණ ඉතිරියි"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ක් පමණ ඉතිරියි (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME_REMAINING">%1$s</xliff:g> පමණ ඉතිරිව ඇත"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 577a6c1ff15c..1c8293312871 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíjanie je pozastavené, aby sa chránila batéria"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – skontrolujte nabíjacie príslušenstvo"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ešte približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> – závisí to od intenzity využitia"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 7acd64d1ab4e..f97dd78f8179 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Zaradi zaščite baterije je polnjenje na čakanju"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Preverite pripomoček za polnjenje"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Glede na način uporabe še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index a2b2042db952..a802f115da4a 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi është vendosur në pritje për të mbrojtur baterinë"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kontrollo aksesorin e karikimit"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura bazuar në përdorimin tënd"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 14dd7720ce79..df2215911673 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је на чекању да би се заштитила батерија"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Проверите додатну опрему за пуњење"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g> на основу коришћења"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index c9262e9d2494..79b8399527a0 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har pausats för att skydda batteriet"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Kontrollera laddningstillbehöret"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar utifrån din användning"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 92c9c7c573cd..c0ace5805e63 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Imesitisha kuchaji ili kulinda betri yako"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kagua kifaa cha kuchaji"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Zimesalia takribani <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Zimesalia takribani <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Zimesalia takribani <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kulingana na jinsi unavyoitumia"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index ec73fbed7264..ebca26dc3063 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - பேட்டரியைப் பாதுகாப்பதற்காகச் சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் துணைக்கருவியைச் சரிபாருங்கள்"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"உபயோகத்தின் அடிப்படையில் கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 0ffdf6a680ae..cf3c08acc54c 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీని రక్షించడానికి ఛార్జింగ్ హోల్డ్లో ఉంచబడింది"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ యాక్సెసరీని ఎంచుకోండి"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"దాదాపు <xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"మీ వినియోగం ఆధారంగా దాదాపు <xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 7bccc25c65dd..a74265ee3e9a 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - หยุดการชาร์จชั่วคราวเพื่อถนอมแบตเตอรี่"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ตรวจสอบอุปกรณ์เสริมสำหรับการชาร์จ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"เหลืออีกประมาณ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"เหลืออีกประมาณ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"เหลืออีกประมาณ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ขึ้นอยู่กับการใช้งานของคุณ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 19ba24fcbda4..18d3d3b626fa 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-hold ang pag-charge para protektahan ang baterya"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Suriin ang accessory sa pag-charge"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira batay sa iyong paggamit"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 80d2fe294ea3..c4241150ad12 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pili korumak için şarj beklemede"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarını kontrol edin"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Kullanımınıza dayalı olarak yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index b1c64f8222b3..97592f970b67 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання призупинено, щоб захистити акумулятор"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – перевірте зарядний пристрій"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Згідно з даними про використання залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index f71f4a2174b1..9d3405234e5b 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری کی حفاظت کرنے کے لیے چارجنگ ہولڈ پر ہے"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ ایکسیسری چیک کریں"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index a3ef6b64d9f9..bd38bb44df6e 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyani himoyalash uchun quvvatlash toʻxtatildi"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quvvatlash aksessuarini tekshiring"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Quvvati tugashiga taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 3ac91fac6cc0..5b91df3e44a2 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang tạm ngưng sạc để bảo vệ pin"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hãy kiểm tra phụ kiện sạc"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 7ff6ceec5fa1..594922a4050d 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 为保护电池,已暂停充电"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 请检查充电配件"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根据您的使用情况,大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 53b55bba0bd0..b3aafae4a3c6 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 檢查充電配件"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根據你的使用情況,還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index f9ee07d07fd0..c3be21a93d8c 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> · 請檢查充電配件"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"目前電量為 <xliff:g id="LEVEL">%2$s</xliff:g>,還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根據你的使用情形,還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 8ff64d141b94..f6aaf813af84 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -457,8 +457,7 @@ <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kumisiwe ukuze kuvikelwe ibhethri"</string> - <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) --> - <skip /> + <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hlola insiza yokushaja"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele ngokususelwe ekusebenziseni wakho"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index bdb58719b1a8..5f026c451152 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -74,6 +74,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -84,6 +85,7 @@ public abstract class InfoMediaManager extends MediaManager { private static final String TAG = "InfoMediaManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); /** Checked exception that signals the specified package is not present in the system. */ public static class PackageNotAvailableException extends Exception { @@ -227,6 +229,16 @@ public abstract class InfoMediaManager extends MediaManager { Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap); } + protected final MediaDevice findMediaDevice(@NonNull String id) { + for (MediaDevice mediaDevice : mMediaDevices) { + if (mediaDevice.getId().equals(id)) { + return mediaDevice; + } + } + Log.e(TAG, "findMediaDevice() can't find device with id: " + id); + return null; + } + /** * Get current device that played media. * @return MediaDevice @@ -433,7 +445,7 @@ public abstract class InfoMediaManager extends MediaManager { protected final synchronized void refreshDevices() { rebuildDeviceList(); - dispatchDeviceListAdded(); + dispatchDeviceListAdded(mMediaDevices); } // MediaRoute2Info.getType was made public on API 34, but exists since API 30. diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java index 8bebd6ee3448..d562c8a82f2d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java @@ -15,9 +15,9 @@ */ package com.android.settingslib.media; +import android.annotation.NonNull; import android.app.Notification; import android.content.Context; -import android.util.Log; import java.util.ArrayList; import java.util.Collection; @@ -29,10 +29,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public abstract class MediaManager { - private static final String TAG = "MediaManager"; - protected final Collection<MediaDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>(); - protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); protected Context mContext; protected Notification mNotification; @@ -54,19 +51,9 @@ public abstract class MediaManager { } } - protected MediaDevice findMediaDevice(String id) { - for (MediaDevice mediaDevice : mMediaDevices) { - if (mediaDevice.getId().equals(id)) { - return mediaDevice; - } - } - Log.e(TAG, "findMediaDevice() can't found device"); - return null; - } - - protected void dispatchDeviceListAdded() { + protected void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) { for (MediaDeviceCallback callback : getCallbacks()) { - callback.onDeviceListAdded(new ArrayList<>(mMediaDevices)); + callback.onDeviceListAdded(new ArrayList<>(devices)); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt index 65b73caeada7..0117ece888c0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt @@ -46,7 +46,7 @@ open class WifiUtils { /** * Wrapper the [.getInternetIconResource] for testing compatibility. */ - class InternetIconInjector(protected val context: Context) { + open class InternetIconInjector(protected val context: Context) { /** * Returns the Internet icon for a given RSSI level. * diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java index 46e724d245f5..c3237f0e72eb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java @@ -16,7 +16,6 @@ package com.android.settingslib.media; -import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; @@ -32,6 +31,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Collections; + @RunWith(RobolectricTestRunner.class) public class MediaManagerTest { @@ -59,7 +60,7 @@ public class MediaManagerTest { public void dispatchDeviceListAdded_registerCallback_shouldDispatchCallback() { mMediaManager.registerCallback(mCallback); - mMediaManager.dispatchDeviceListAdded(); + mMediaManager.dispatchDeviceListAdded(Collections.emptyList()); verify(mCallback).onDeviceListAdded(any()); } @@ -68,9 +69,9 @@ public class MediaManagerTest { public void dispatchDeviceListRemoved_registerCallback_shouldDispatchCallback() { mMediaManager.registerCallback(mCallback); - mMediaManager.dispatchDeviceListRemoved(mMediaManager.mMediaDevices); + mMediaManager.dispatchDeviceListRemoved(Collections.emptyList()); - verify(mCallback).onDeviceListRemoved(mMediaManager.mMediaDevices); + verify(mCallback).onDeviceListRemoved(Collections.emptyList()); } @Test @@ -83,24 +84,6 @@ public class MediaManagerTest { } @Test - public void findMediaDevice_idExist_shouldReturnMediaDevice() { - mMediaManager.mMediaDevices.add(mDevice); - - final MediaDevice device = mMediaManager.findMediaDevice(TEST_ID); - - assertThat(device.getId()).isEqualTo(mDevice.getId()); - } - - @Test - public void findMediaDevice_idNotExist_shouldReturnNull() { - mMediaManager.mMediaDevices.add(mDevice); - - final MediaDevice device = mMediaManager.findMediaDevice("123"); - - assertThat(device).isNull(); - } - - @Test public void dispatchOnRequestFailed_registerCallback_shouldDispatchCallback() { mMediaManager.registerCallback(mCallback); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 2fa1c6eda458..a490b6f096ef 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -154,6 +154,7 @@ public class SecureSettings { Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, + Settings.Secure.EMERGENCY_THERMAL_ALERT_DISABLED, Settings.Secure.HUSH_GESTURE_USED, Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 2535fdb6e4d0..4cdf98cbe14e 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -235,6 +235,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.MANUAL_RINGER_TOGGLE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.EMERGENCY_THERMAL_ALERT_DISABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.IN_CALL_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index 6ff36d43f91f..ad3eb92b0519 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -20,8 +20,6 @@ import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; -import static com.android.providers.settings.Flags.supportOverrides; - import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.annotation.SuppressLint; @@ -269,9 +267,9 @@ public final class DeviceConfigService extends Binder { verb = CommandVerb.GET; } else if ("put".equalsIgnoreCase(cmd)) { verb = CommandVerb.PUT; - } else if (supportOverrides() && "override".equalsIgnoreCase(cmd)) { + } else if ("override".equalsIgnoreCase(cmd)) { verb = CommandVerb.OVERRIDE; - } else if (supportOverrides() && "clear_override".equalsIgnoreCase(cmd)) { + } else if ("clear_override".equalsIgnoreCase(cmd)) { verb = CommandVerb.CLEAR_OVERRIDE; } else if ("delete".equalsIgnoreCase(cmd)) { verb = CommandVerb.DELETE; @@ -285,7 +283,7 @@ public final class DeviceConfigService extends Binder { if (peekNextArg() == null) { isValid = true; } - } else if (supportOverrides() && "list_local_overrides".equalsIgnoreCase(cmd)) { + } else if ("list_local_overrides".equalsIgnoreCase(cmd)) { verb = CommandVerb.LIST_LOCAL_OVERRIDES; if (peekNextArg() == null) { isValid = true; @@ -427,14 +425,10 @@ public final class DeviceConfigService extends Binder { DeviceConfig.setProperty(namespace, key, value, makeDefault); break; case OVERRIDE: - if (supportOverrides()) { - DeviceConfig.setLocalOverride(namespace, key, value); - } + DeviceConfig.setLocalOverride(namespace, key, value); break; case CLEAR_OVERRIDE: - if (supportOverrides()) { - DeviceConfig.clearLocalOverride(namespace, key); - } + DeviceConfig.clearLocalOverride(namespace, key); break; case DELETE: pout.println(delete(iprovider, namespace, key) @@ -452,19 +446,15 @@ public final class DeviceConfigService extends Binder { } } else { for (String line : listAll(iprovider)) { - if (supportOverrides()) { - boolean isPrivate = false; - for (String privateNamespace : PRIVATE_NAMESPACES) { - if (line.startsWith(privateNamespace)) { - isPrivate = true; - break; - } + boolean isPrivate = false; + for (String privateNamespace : PRIVATE_NAMESPACES) { + if (line.startsWith(privateNamespace)) { + isPrivate = true; + break; } + } - if (!isPrivate) { - pout.println(line); - } - } else { + if (!isPrivate) { pout.println(line); } } @@ -495,18 +485,16 @@ public final class DeviceConfigService extends Binder { } break; case LIST_LOCAL_OVERRIDES: - if (supportOverrides()) { - Map<String, Map<String, String>> underlyingValues = - DeviceConfig.getUnderlyingValuesForOverriddenFlags(); - for (String overrideNamespace : underlyingValues.keySet()) { - Map<String, String> flagToValue = - underlyingValues.get(overrideNamespace); - for (String flag : flagToValue.keySet()) { - String flagText = overrideNamespace + "/" + flag; - String valueText = - DeviceConfig.getProperty(overrideNamespace, flag); - pout.println(flagText + "=" + valueText); - } + Map<String, Map<String, String>> underlyingValues = + DeviceConfig.getUnderlyingValuesForOverriddenFlags(); + for (String overrideNamespace : underlyingValues.keySet()) { + Map<String, String> flagToValue = + underlyingValues.get(overrideNamespace); + for (String flag : flagToValue.keySet()) { + String flagText = overrideNamespace + "/" + flag; + String valueText = + DeviceConfig.getProperty(overrideNamespace, flag); + pout.println(flagText + "=" + valueText); } } break; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index febce97031bb..1ead14ab6f4c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3812,7 +3812,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 225; + private static final int SETTINGS_VERSION = 226; private final int mUserId; @@ -6011,6 +6011,28 @@ public class SettingsProvider extends ContentProvider { currentVersion = 225; } + // Version 225: Set the System#KEYBOARD_VIBRATION_ENABLED based on touch + // feedback enabled state. + if (currentVersion == 225) { + final SettingsState systemSettings = getSystemSettingsLocked(userId); + final Setting touchFeedbackSettings = systemSettings + .getSettingLocked(Settings.System.HAPTIC_FEEDBACK_ENABLED); + final Setting keyboardVibrationSettings = systemSettings + .getSettingLocked(Settings.System.KEYBOARD_VIBRATION_ENABLED); + if (keyboardVibrationSettings.isNull()) { + if (!touchFeedbackSettings.isNull()) { + // Use touch feedback settings. + systemSettings.insertSettingOverrideableByRestoreLocked( + Settings.System.KEYBOARD_VIBRATION_ENABLED, + touchFeedbackSettings.getValue(), + touchFeedbackSettings.getTag(), + touchFeedbackSettings.isDefaultFromSystem(), + SettingsState.SYSTEM_PACKAGE_NAME); + } + } + currentVersion = 226; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 085fc2959442..88181e7e2a52 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -56,10 +56,12 @@ public class AccessibilityMenuService extends AccessibilityService implements View.OnTouchListener { public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName(); + public static final String PACKAGE_TESTS = ".tests"; public static final String INTENT_TOGGLE_MENU = ".toggle_menu"; public static final String INTENT_HIDE_MENU = ".hide_menu"; public static final String INTENT_GLOBAL_ACTION = ".global_action"; public static final String INTENT_GLOBAL_ACTION_EXTRA = "GLOBAL_ACTION"; + public static final String INTENT_OPEN_BLOCKED = "OPEN_BLOCKED"; private static final String TAG = "A11yMenuService"; private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L; @@ -192,7 +194,7 @@ public class AccessibilityMenuService extends AccessibilityService IntentFilter hideMenuFilter = new IntentFilter(); hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF); - hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU); + hideMenuFilter.addAction(INTENT_HIDE_MENU); // Including WRITE_SECURE_SETTINGS enforces that we only listen to apps // with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent. @@ -200,7 +202,7 @@ public class AccessibilityMenuService extends AccessibilityService Manifest.permission.WRITE_SECURE_SETTINGS, null, Context.RECEIVER_EXPORTED); registerReceiver(mToggleMenuReceiver, - new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU), + new IntentFilter(INTENT_TOGGLE_MENU), Manifest.permission.WRITE_SECURE_SETTINGS, null, Context.RECEIVER_EXPORTED); @@ -245,8 +247,9 @@ public class AccessibilityMenuService extends AccessibilityService * @return {@code true} if successful, {@code false} otherwise. */ private boolean performGlobalActionInternal(int globalAction) { - Intent intent = new Intent(PACKAGE_NAME + INTENT_GLOBAL_ACTION); + Intent intent = new Intent(INTENT_GLOBAL_ACTION); intent.putExtra(INTENT_GLOBAL_ACTION_EXTRA, globalAction); + intent.setPackage(PACKAGE_NAME + PACKAGE_TESTS); sendBroadcast(intent); Log.i("A11yMenuService", "Broadcasting global action " + globalAction); return performGlobalAction(globalAction); @@ -410,9 +413,16 @@ public class AccessibilityMenuService extends AccessibilityService private void toggleVisibility() { boolean locked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); - if (!locked && SystemClock.uptimeMillis() - mLastTimeTouchedOutside - > BUTTON_CLICK_TIMEOUT) { - mA11yMenuLayout.toggleVisibility(); + if (!locked) { + if (SystemClock.uptimeMillis() - mLastTimeTouchedOutside + > BUTTON_CLICK_TIMEOUT) { + mA11yMenuLayout.toggleVisibility(); + } + } else { + // Broadcast for testing. + Intent intent = new Intent(INTENT_OPEN_BLOCKED); + intent.setPackage(PACKAGE_NAME + PACKAGE_TESTS); + sendBroadcast(intent); } } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java index 72c1092b92d6..6546b87c8802 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java @@ -23,6 +23,7 @@ import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_QU import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_RECENTS; import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; +import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_OPEN_BLOCKED; import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION; import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION_EXTRA; import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU; @@ -65,6 +66,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @RunWith(AndroidJUnit4.class) @@ -75,12 +77,11 @@ public class AccessibilityMenuServiceTest { private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5; private static final int TIMEOUT_UI_CHANGE_S = 5; private static final int NO_GLOBAL_ACTION = -1; - private static final String INPUT_KEYEVENT_KEYCODE_BACK = "input keyevent KEYCODE_BACK"; - private static final String TEST_PIN = "1234"; private static Instrumentation sInstrumentation; private static UiAutomation sUiAutomation; - private static AtomicInteger sLastGlobalAction; + private static final AtomicInteger sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION); + private static final AtomicBoolean sOpenBlocked = new AtomicBoolean(false); private static AccessibilityManager sAccessibilityManager; private static PowerManager sPowerManager; @@ -122,8 +123,6 @@ public class AccessibilityMenuServiceTest { () -> sAccessibilityManager.getEnabledAccessibilityServiceList( AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter( info -> info.getId().contains(serviceName)).count() == 1); - - sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION); context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -131,20 +130,28 @@ public class AccessibilityMenuServiceTest { sLastGlobalAction.set( intent.getIntExtra(INTENT_GLOBAL_ACTION_EXTRA, NO_GLOBAL_ACTION)); }}, - new IntentFilter(PACKAGE_NAME + INTENT_GLOBAL_ACTION), + new IntentFilter(INTENT_GLOBAL_ACTION), + null, null, Context.RECEIVER_EXPORTED); + + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.i(TAG, "Received notification that menu cannot be opened."); + sOpenBlocked.set(true); + }}, + new IntentFilter(INTENT_OPEN_BLOCKED), null, null, Context.RECEIVER_EXPORTED); } @AfterClass - public static void classTeardown() throws Throwable { - clearPin(); + public static void classTeardown() { Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, ""); } @Before public void setup() throws Throwable { - clearPin(); + sOpenBlocked.set(false); wakeUpScreen(); sUiAutomation.executeShellCommand("input keyevent KEYCODE_MENU"); openMenu(); @@ -154,20 +161,8 @@ public class AccessibilityMenuServiceTest { public void tearDown() throws Throwable { closeMenu(); sLastGlobalAction.set(NO_GLOBAL_ACTION); - } - - private static void clearPin() throws Throwable { - sUiAutomation.executeShellCommand("locksettings clear --old " + TEST_PIN); - TestUtils.waitUntil("Device did not register as unlocked & insecure.", - TIMEOUT_SERVICE_STATUS_CHANGE_S, - () -> !sKeyguardManager.isDeviceSecure()); - } - - private static void setPin() throws Throwable { - sUiAutomation.executeShellCommand("locksettings set-pin " + TEST_PIN); - TestUtils.waitUntil("Device did not recognize as locked & secure.", - TIMEOUT_SERVICE_STATUS_CHANGE_S, - () -> sKeyguardManager.isDeviceSecure()); + // dismisses screenshot popup if present. + sUiAutomation.executeShellCommand("input keyevent KEYCODE_BACK"); } private static boolean isMenuVisible() { @@ -184,7 +179,6 @@ public class AccessibilityMenuServiceTest { private static void closeScreen() throws Throwable { Display display = sDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); - setPin(); sUiAutomation.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN); TestUtils.waitUntil("Screen did not close.", TIMEOUT_UI_CHANGE_S, @@ -194,12 +188,20 @@ public class AccessibilityMenuServiceTest { } private static void openMenu() throws Throwable { - Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU); + openMenu(false); + } + + private static void openMenu(boolean abandonOnBlock) throws Throwable { + Intent intent = new Intent(INTENT_TOGGLE_MENU); + intent.setPackage(PACKAGE_NAME); sInstrumentation.getContext().sendBroadcast(intent); TestUtils.waitUntil("Timed out before menu could appear.", TIMEOUT_UI_CHANGE_S, () -> { + if (sOpenBlocked.get() && abandonOnBlock) { + throw new IllegalStateException(); + } if (isMenuVisible()) { return true; } else { @@ -213,7 +215,8 @@ public class AccessibilityMenuServiceTest { if (!isMenuVisible()) { return; } - Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU); + Intent intent = new Intent(INTENT_HIDE_MENU); + intent.setPackage(PACKAGE_NAME); sInstrumentation.getContext().sendBroadcast(intent); TestUtils.waitUntil("Timed out before menu could close.", TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible()); @@ -444,13 +447,13 @@ public class AccessibilityMenuServiceTest { closeScreen(); wakeUpScreen(); - boolean timedOut = false; + boolean blocked = false; try { - openMenu(); - } catch (AssertionError e) { + openMenu(true); + } catch (IllegalStateException e) { // Expected - timedOut = true; + blocked = true; } - assertThat(timedOut).isTrue(); + assertThat(blocked).isTrue(); } } diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig index d0e6b2865891..7bbe82c212e7 100644 --- a/packages/SystemUI/aconfig/predictive_back.aconfig +++ b/packages/SystemUI/aconfig/predictive_back.aconfig @@ -4,26 +4,26 @@ flag { name: "predictive_back_sysui" namespace: "systemui" description: "Predictive Back Dispatching for SysUI" - bug: "309545085" + bug: "327737297" } flag { name: "predictive_back_animate_shade" namespace: "systemui" description: "Enable Shade Animations" - bug: "309545085" + bug: "327732946" } flag { name: "predictive_back_animate_bouncer" namespace: "systemui" description: "Enable Predictive Back Animation in Bouncer" - bug: "309545085" + bug: "327733487" } flag { name: "predictive_back_animate_dialogs" namespace: "systemui" description: "Enable Predictive Back Animation for SysUI dialogs" - bug: "309545085" + bug: "327721544" }
\ No newline at end of file diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index 0469cbe519ea..3ec5508c81b3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -22,15 +22,17 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.Back import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -52,13 +54,13 @@ constructor( private val viewModel: BouncerViewModel, private val dialogFactory: BouncerDialogFactory, ) : ComposableScene { - override val key = SceneKey.Bouncer + override val key = Scenes.Bouncer override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = MutableStateFlow( mapOf( - UserAction.Back to UserActionResult(SceneKey.Lockscreen), - UserAction.Swipe(Direction.DOWN) to UserActionResult(SceneKey.Lockscreen), + Back to UserActionResult(Scenes.Lockscreen), + Swipe(SwipeDirection.Down) to UserActionResult(Scenes.Lockscreen), ) ) .asStateFlow() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index 7535a51675e3..9ee69bc065f6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -14,7 +14,6 @@ import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.FixedSizeEdgeDetector import com.android.compose.animation.scene.LowestZIndexScenePicker -import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout @@ -24,14 +23,11 @@ import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.compose.theme.LocalAndroidColorScheme -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.compose.extensions.allowGestures import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.res.R -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.transform object Communal { object Elements { @@ -41,7 +37,7 @@ object Communal { } val sceneTransitions = transitions { - to(TransitionSceneKey.Communal) { + to(CommunalScenes.Communal) { spec = tween(durationMillis = 1000) translate(Communal.Elements.Content, Edge.Right) timestampRange(startMillis = 167, endMillis = 334) { @@ -49,7 +45,7 @@ val sceneTransitions = transitions { fade(Communal.Elements.Content) } } - to(TransitionSceneKey.Blank) { + to(CommunalScenes.Blank) { spec = tween(durationMillis = 1000) translate(Communal.Elements.Content, Edge.Right) timestampRange(endMillis = 167) { fade(Communal.Elements.Content) } @@ -68,14 +64,11 @@ fun CommunalContainer( modifier: Modifier = Modifier, viewModel: CommunalViewModel, ) { - val currentScene: SceneKey by - viewModel.currentScene - .transform { value -> emit(value.toTransitionSceneKey()) } - .collectAsState(TransitionSceneKey.Blank) + val currentScene: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank) val sceneTransitionLayoutState = updateSceneTransitionLayoutState( currentScene, - onChangeScene = { viewModel.onSceneChanged(it.toCommunalSceneKey()) }, + onChangeScene = { viewModel.onSceneChanged(it) }, transitions = sceneTransitions, ) val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false) @@ -83,9 +76,7 @@ fun CommunalContainer( // This effect exposes the SceneTransitionLayout's observable transition state to the rest of // the system, and unsets it when the view is disposed to avoid a memory leak. DisposableEffect(viewModel, sceneTransitionLayoutState) { - viewModel.setTransitionState( - sceneTransitionLayoutState.observableTransitionState().map { it.toModel() } - ) + viewModel.setTransitionState(sceneTransitionLayoutState.observableTransitionState()) onDispose { viewModel.setTransitionState(null) } } @@ -98,11 +89,10 @@ fun CommunalContainer( ), ) { scene( - TransitionSceneKey.Blank, + CommunalScenes.Blank, userActions = mapOf( - Swipe(SwipeDirection.Left, fromSource = Edge.Right) to - TransitionSceneKey.Communal + Swipe(SwipeDirection.Left, fromSource = Edge.Right) to CommunalScenes.Communal ) ) { // This scene shows nothing only allowing for transitions to the communal scene. @@ -110,11 +100,9 @@ fun CommunalContainer( } scene( - TransitionSceneKey.Communal, + CommunalScenes.Communal, userActions = - mapOf( - Swipe(SwipeDirection.Right, fromSource = Edge.Left) to TransitionSceneKey.Blank - ), + mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank), ) { CommunalScene(viewModel, modifier = modifier) } @@ -135,39 +123,3 @@ private fun SceneScope.CommunalScene( ) Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) } } - -// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI. -object TransitionSceneKey { - val Blank = CommunalSceneKey.Blank.toTransitionSceneKey() - val Communal = CommunalSceneKey.Communal.toTransitionSceneKey() -} - -// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI. -fun SceneKey.toCommunalSceneKey(): CommunalSceneKey { - return this.identity as CommunalSceneKey -} - -// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI. -fun CommunalSceneKey.toTransitionSceneKey(): SceneKey { - return SceneKey(debugName = toString(), identity = this) -} - -/** - * Converts between the [SceneTransitionLayout] state class and our forked data class that can be - * used throughout SysUI. - */ -// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI. -fun ObservableTransitionState.toModel(): ObservableCommunalTransitionState { - return when (this) { - is ObservableTransitionState.Idle -> - ObservableCommunalTransitionState.Idle(scene.toCommunalSceneKey()) - is ObservableTransitionState.Transition -> - ObservableCommunalTransitionState.Transition( - fromScene = fromScene.toCommunalSceneKey(), - toScene = toScene.toCommunalSceneKey(), - progress = progress, - isInitiatedByUserInput = isInitiatedByUserInput, - isUserInputOngoing = isUserInputOngoing, - ) - } -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 078da1c863ce..515c8169f1c4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -31,8 +31,8 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -93,6 +93,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId @@ -118,6 +119,7 @@ import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset import com.android.systemui.communal.ui.compose.extensions.observeTapsWithoutConsuming import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel +import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import kotlinx.coroutines.launch @@ -197,26 +199,37 @@ fun CommunalHub( } }, ) { - CommunalHubLazyGrid( - communalContent = communalContent, - viewModel = viewModel, - contentPadding = contentPadding, - contentOffset = contentOffset, - setGridCoordinates = { gridCoordinates = it }, - updateDragPositionForRemove = { offset -> - isDraggingToRemove = - isPointerWithinCoordinates( - offset = gridCoordinates?.let { it.positionInWindow() + offset }, - containerToCheck = removeButtonCoordinates - ) - isDraggingToRemove - }, - onOpenWidgetPicker = onOpenWidgetPicker, - gridState = gridState, - contentListState = contentListState, - selectedKey = selectedKey, - widgetConfigurator = widgetConfigurator, - ) + Column(Modifier.align(Alignment.TopStart)) { + CommunalHubLazyGrid( + communalContent = communalContent, + viewModel = viewModel, + contentPadding = contentPadding, + contentOffset = contentOffset, + setGridCoordinates = { gridCoordinates = it }, + updateDragPositionForRemove = { offset -> + isDraggingToRemove = + isPointerWithinCoordinates( + offset = gridCoordinates?.let { it.positionInWindow() + offset }, + containerToCheck = removeButtonCoordinates + ) + isDraggingToRemove + }, + onOpenWidgetPicker = onOpenWidgetPicker, + gridState = gridState, + contentListState = contentListState, + selectedKey = selectedKey, + widgetConfigurator = widgetConfigurator, + ) + // TODO(b/326060686): Remove this once keyguard indication area can persist over hub + if (viewModel is CommunalViewModel) { + val isUnlocked by viewModel.deviceUnlocked.collectAsState(initial = false) + Spacer(Modifier.height(24.dp)) + LockStateIcon( + isUnlocked = isUnlocked, + modifier = Modifier.align(Alignment.CenterHorizontally), + ) + } + } if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { Toolbar( @@ -268,7 +281,7 @@ fun CommunalHub( @OptIn(ExperimentalFoundationApi::class) @Composable -private fun BoxScope.CommunalHubLazyGrid( +private fun ColumnScope.CommunalHubLazyGrid( communalContent: List<CommunalContentModel>, viewModel: BaseCommunalViewModel, contentPadding: PaddingValues, @@ -282,7 +295,7 @@ private fun BoxScope.CommunalHubLazyGrid( widgetConfigurator: WidgetConfigurator?, ) { var gridModifier = - Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) } + Modifier.align(Alignment.Start).onGloballyPositioned { setGridCoordinates(it) } var list = communalContent var dragDropState: GridDragDropState? = null if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) { @@ -364,6 +377,26 @@ private fun BoxScope.CommunalHubLazyGrid( } } +@Composable +private fun LockStateIcon( + isUnlocked: Boolean, + modifier: Modifier = Modifier, +) { + val colors = LocalAndroidColorScheme.current + val resource = + if (isUnlocked) { + R.drawable.ic_unlocked + } else { + R.drawable.ic_lock + } + Icon( + painter = painterResource(id = resource), + contentDescription = null, + tint = colors.onPrimaryContainer, + modifier = modifier.size(52.dp) + ) +} + /** * Toolbar that contains action buttons to * 1) open the widget picker diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt index 11a38f92c234..3d88ad53685e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt @@ -19,12 +19,13 @@ package com.android.systemui.communal.ui.compose import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -38,12 +39,12 @@ class CommunalScene constructor( private val viewModel: CommunalViewModel, ) : ComposableScene { - override val key = SceneKey.Communal + override val key = Scenes.Communal override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = MutableStateFlow<Map<UserAction, UserActionResult>>( mapOf( - UserAction.Swipe(Direction.RIGHT) to UserActionResult(SceneKey.Lockscreen), + Swipe(SwipeDirection.Right) to UserActionResult(Scenes.Lockscreen), ) ) .asStateFlow() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt index dd8664696973..a8d801abdcd0 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt @@ -18,8 +18,11 @@ package com.android.systemui.keyboard.stickykeys.ui.view import android.content.Context import android.view.View +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -61,22 +64,32 @@ fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) { @Composable fun StickyKeysIndicator(stickyKeys: Map<ModifierKey, Locked>, modifier: Modifier = Modifier) { Surface( - color = MaterialTheme.colorScheme.surface, + color = MaterialTheme.colorScheme.inverseSurface, shape = MaterialTheme.shapes.medium, - modifier = modifier + modifier = modifier.heightIn(min = 84.dp).width(96.dp) ) { Column( horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, modifier = Modifier.padding(16.dp) ) { - stickyKeys.forEach { (key, isLocked) -> - key(key) { - Text( - text = key.displayedText, - fontWeight = if (isLocked.locked) FontWeight.Bold else FontWeight.Normal - ) - } - } + stickyKeys.forEach { (key, isLocked) -> key(key) { StickyKeyText(key, isLocked) } } } } } + +@Composable +private fun StickyKeyText(key: ModifierKey, isLocked: Locked, modifier: Modifier = Modifier) { + Text( + text = key.displayedText, + fontWeight = if (isLocked.locked) FontWeight.Bold else FontWeight.Normal, + style = MaterialTheme.typography.bodyMedium, + color = + if (isLocked.locked) { + MaterialTheme.colorScheme.inverseOnSurface + } else { + MaterialTheme.colorScheme.outlineVariant + }, + modifier = modifier + ) +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index dd043dbebaa6..a02781ba63f7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -18,19 +18,20 @@ package com.android.systemui.keyguard.ui.composable import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.Edge +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel import com.android.systemui.qs.ui.composable.QuickSettings -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.Edge -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import dagger.Lazy import javax.inject.Inject @@ -50,7 +51,7 @@ constructor( viewModel: LockscreenSceneViewModel, private val lockscreenContent: Lazy<LockscreenContent>, ) : ComposableScene { - override val key = SceneKey.Lockscreen + override val key = Scenes.Lockscreen override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = combine(viewModel.upDestinationSceneKey, viewModel.leftDestinationSceneKey, ::Pair) @@ -80,11 +81,11 @@ constructor( left: SceneKey?, ): Map<UserAction, UserActionResult> { return buildMap { - up?.let { this[UserAction.Swipe(Direction.UP)] = UserActionResult(up) } - left?.let { this[UserAction.Swipe(Direction.LEFT)] = UserActionResult(left) } - this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] = - UserActionResult(SceneKey.QuickSettings) - this[UserAction.Swipe(direction = Direction.DOWN)] = UserActionResult(SceneKey.Shade) + up?.let { this[Swipe(SwipeDirection.Up)] = UserActionResult(up) } + left?.let { this[Swipe(SwipeDirection.Left)] = UserActionResult(left) } + this[Swipe(fromSource = Edge.Top, direction = SwipeDirection.Down)] = + UserActionResult(Scenes.QuickSettings) + this[Swipe(direction = SwipeDirection.Down)] = UserActionResult(Scenes.Shade) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index ef6ae2ecfec9..791d629179e6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -71,8 +71,7 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadi import com.android.systemui.notifications.ui.composable.Notifications.Form import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA -import com.android.systemui.scene.ui.composable.Gone -import com.android.systemui.scene.ui.composable.Shade +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationStackAppearanceViewBinder.SCRIM_CORNER_RADIUS import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel @@ -214,7 +213,7 @@ fun SceneScope.NotificationScrollingStack( // in step with the transition so that it is 0 when it completes. if ( scrimOffset.value < 0 && - layoutState.isTransitioning(from = Shade, to = Gone) + layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Gone) ) { IntOffset(x = 0, y = (scrimOffset.value * expansionFraction).roundToInt()) } else { @@ -226,7 +225,7 @@ fun SceneScope.NotificationScrollingStack( calculateCornerRadius( screenCornerRadius, { expansionFraction }, - layoutState.isTransitioningBetween(Gone, Shade) + layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ) .let { RoundedCornerShape( @@ -250,7 +249,7 @@ fun SceneScope.NotificationScrollingStack( Modifier.fillMaxSize() .graphicsLayer { alpha = - if (layoutState.isTransitioningBetween(Gone, Shade)) { + if (layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade)) { (expansionFraction / EXPANSION_FOR_MAX_SCRIM_ALPHA).coerceAtMost(1f) } else 1f } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index 5d0b9ba2c736..91b737d33418 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -37,14 +37,13 @@ import com.android.systemui.qs.ui.adapter.QSSceneAdapter import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Unsquishing -import com.android.systemui.scene.ui.composable.QuickSettings as QuickSettingsSceneKey -import com.android.systemui.scene.ui.composable.Shade +import com.android.systemui.scene.shared.model.Scenes object QuickSettings { private val SCENES = setOf( - QuickSettingsSceneKey, - Shade, + Scenes.QuickSettings, + Scenes.Shade, ) object Elements { @@ -69,18 +68,20 @@ private fun SceneScope.stateForQuickSettingsContent( return when (val transitionState = layoutState.transitionState) { is TransitionState.Idle -> { when (transitionState.currentScene) { - Shade -> QSSceneAdapter.State.QQS - QuickSettingsSceneKey -> QSSceneAdapter.State.QS + Scenes.Shade -> QSSceneAdapter.State.QQS + Scenes.QuickSettings -> QSSceneAdapter.State.QS else -> QSSceneAdapter.State.CLOSED } } is TransitionState.Transition -> with(transitionState) { when { - fromScene == Shade && toScene == QuickSettingsSceneKey -> Expanding(progress) - fromScene == QuickSettingsSceneKey && toScene == Shade -> Collapsing(progress) - fromScene == Shade || toScene == Shade -> Unsquishing(squishiness) - fromScene == QuickSettingsSceneKey || toScene == QuickSettingsSceneKey -> { + fromScene == Scenes.Shade && toScene == Scenes.QuickSettings -> + Expanding(progress) + fromScene == Scenes.QuickSettings && toScene == Scenes.Shade -> + Collapsing(progress) + fromScene == Scenes.Shade || toScene == Scenes.Shade -> Unsquishing(squishiness) + fromScene == Scenes.QuickSettings || toScene == Scenes.QuickSettings -> { QSSceneAdapter.State.QS } else -> diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 6875bc544a55..3b8b863fdde2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -63,9 +63,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qs.footer.ui.compose.FooterActions import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel import com.android.systemui.res.R -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene -import com.android.systemui.scene.ui.composable.asComposeAware import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.ExpandedShadeHeader import com.android.systemui.shade.ui.composable.Shade @@ -89,7 +88,7 @@ constructor( private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory, private val statusBarIconController: StatusBarIconController, ) : ComposableScene { - override val key = SceneKey.QuickSettings + override val key = Scenes.QuickSettings override val destinationScenes = viewModel.destinationScenes.stateIn( @@ -140,9 +139,7 @@ private fun SceneScope.QuickSettingsScene( val isScrollable = when (val state = layoutState.transitionState) { is TransitionState.Idle -> true - is TransitionState.Transition -> { - state.fromScene == SceneKey.QuickSettings.asComposeAware() - } + is TransitionState.Transition -> state.fromScene == Scenes.QuickSettings } LaunchedEffect(isCustomizing, scrollState) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt deleted file mode 100644 index 0de4650f1248..000000000000 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2024 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.scene.ui.composable - -import com.android.compose.animation.scene.Back -import com.android.compose.animation.scene.Edge as ComposeAwareEdge -import com.android.compose.animation.scene.SceneKey as ComposeAwareSceneKey -import com.android.compose.animation.scene.Swipe -import com.android.compose.animation.scene.SwipeDirection -import com.android.compose.animation.scene.TransitionKey as ComposeAwareTransitionKey -import com.android.compose.animation.scene.UserAction as ComposeAwareUserAction -import com.android.compose.animation.scene.UserActionResult as ComposeAwareUserActionResult -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.Edge -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.TransitionKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult - -// TODO(b/293899074): remove this file once we can use the types from SceneTransitionLayout. - -fun SceneKey.asComposeAware(): ComposeAwareSceneKey { - return ComposeAwareSceneKey( - debugName = toString(), - identity = this, - ) -} - -fun TransitionKey.asComposeAware(): ComposeAwareTransitionKey { - return ComposeAwareTransitionKey( - debugName = debugName, - identity = this, - ) -} - -fun UserAction.asComposeAware(): ComposeAwareUserAction { - return when (this) { - is UserAction.Swipe -> - Swipe( - pointerCount = pointerCount, - fromSource = - when (this.fromEdge) { - null -> null - Edge.LEFT -> ComposeAwareEdge.Left - Edge.TOP -> ComposeAwareEdge.Top - Edge.RIGHT -> ComposeAwareEdge.Right - Edge.BOTTOM -> ComposeAwareEdge.Bottom - }, - direction = - when (this.direction) { - Direction.LEFT -> SwipeDirection.Left - Direction.UP -> SwipeDirection.Up - Direction.RIGHT -> SwipeDirection.Right - Direction.DOWN -> SwipeDirection.Down - } - ) - is UserAction.Back -> Back - } -} - -fun UserActionResult.asComposeAware(): ComposeAwareUserActionResult { - val composeUnaware = this - return ComposeAwareUserActionResult( - toScene = composeUnaware.toScene.asComposeAware(), - transitionKey = composeUnaware.transitionKey?.asComposeAware(), - ) -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt deleted file mode 100644 index 4c03664fc244..000000000000 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 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.scene.ui.composable - -import com.android.compose.animation.scene.ObservableTransitionState as ComposeAwareObservableTransitionState -import com.android.compose.animation.scene.SceneKey as ComposeAwareSceneKey -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey - -fun ComposeAwareSceneKey.asComposeUnaware(): SceneKey { - return this.identity as SceneKey -} - -fun ComposeAwareObservableTransitionState.asComposeUnaware(): ObservableTransitionState { - return when (this) { - is ComposeAwareObservableTransitionState.Idle -> - ObservableTransitionState.Idle(scene.asComposeUnaware()) - is ComposeAwareObservableTransitionState.Transition -> - ObservableTransitionState.Transition( - fromScene = fromScene.asComposeUnaware(), - toScene = toScene.asComposeUnaware(), - progress = progress, - isInitiatedByUserInput = isInitiatedByUserInput, - isUserInputOngoing = isUserInputOngoing, - ) - } -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index 9ca751e81eed..82f56abbded6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -19,17 +19,17 @@ package com.android.systemui.scene.ui.composable import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.ui.composable.QuickSettings -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.Edge -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -46,18 +46,17 @@ class GoneScene constructor( private val notificationsViewModel: NotificationsPlaceholderViewModel, ) : ComposableScene { - override val key = SceneKey.Gone + override val key = Scenes.Gone override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = MutableStateFlow<Map<UserAction, UserActionResult>>( mapOf( - UserAction.Swipe( + Swipe( pointerCount = 2, - fromEdge = Edge.TOP, - direction = Direction.DOWN, - ) to UserActionResult(SceneKey.QuickSettings), - UserAction.Swipe(direction = Direction.DOWN) to - UserActionResult(SceneKey.Shade), + fromSource = Edge.Top, + direction = SwipeDirection.Down, + ) to UserActionResult(Scenes.QuickSettings), + Swipe(direction = SwipeDirection.Down) to UserActionResult(Scenes.Shade), ) ) .asStateFlow() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 9779d7170d0d..0fdaabe75306 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -35,15 +35,14 @@ import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.motionEventSpy import androidx.compose.ui.input.pointer.pointerInput import com.android.compose.animation.scene.MutableSceneTransitionLayoutState +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayout +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.SceneDataSourceDelegator -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import kotlinx.coroutines.flow.map /** * Renders a container of a collection of "scenes" that the user can switch between using certain @@ -77,8 +76,8 @@ fun SceneContainer( currentScene.destinationScenes.collectAsState() val state: MutableSceneTransitionLayoutState = remember { MutableSceneTransitionLayoutState( - initialScene = currentSceneKey.asComposeAware(), - canChangeScene = { toScene -> viewModel.canChangeScene(toScene.asComposeUnaware()) }, + initialScene = currentSceneKey, + canChangeScene = { toScene -> viewModel.canChangeScene(toScene) }, transitions = SceneContainerTransitions, ) } @@ -90,9 +89,7 @@ fun SceneContainer( } DisposableEffect(viewModel, state) { - viewModel.setTransitionState( - state.observableTransitionState().map { it.asComposeUnaware() } - ) + viewModel.setTransitionState(state.observableTransitionState()) onDispose { viewModel.setTransitionState(null) } } @@ -116,23 +113,17 @@ fun SceneContainer( ) { sceneByKey.forEach { (sceneKey, composableScene) -> scene( - key = sceneKey.asComposeAware(), + key = sceneKey, userActions = if (sceneKey == currentSceneKey) { - currentDestinations - } else { - composableScene.destinationScenes.value - } - .map { (userAction, userActionResult) -> - userAction.asComposeAware() to userActionResult.asComposeAware() - } - .toMap(), + currentDestinations + } else { + composableScene.destinationScenes.value + }, ) { with(composableScene) { this@scene.Content( - modifier = - Modifier.element(sceneKey.asComposeAware().rootElementKey) - .fillMaxSize(), + modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(), ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt index 61f81209ad7e..dea9485e916c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt @@ -1,6 +1,7 @@ package com.android.systemui.scene.ui.composable import com.android.compose.animation.scene.transitions +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.CollapseShadeInstantly import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition @@ -26,41 +27,41 @@ import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettings * Please keep the list sorted alphabetically. */ val SceneContainerTransitions = transitions { - from(Bouncer, to = Gone) { bouncerToGoneTransition() } - from(Gone, to = Shade) { goneToShadeTransition() } + from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() } + from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() } from( - Gone, - to = Shade, - key = CollapseShadeInstantly.asComposeAware(), + Scenes.Gone, + to = Scenes.Shade, + key = CollapseShadeInstantly, ) { goneToShadeTransition(durationScale = 0.0) } from( - Gone, - to = Shade, - key = SlightlyFasterShadeCollapse.asComposeAware(), + Scenes.Gone, + to = Scenes.Shade, + key = SlightlyFasterShadeCollapse, ) { goneToShadeTransition(durationScale = 0.9) } - from(Gone, to = QuickSettings) { goneToQuickSettingsTransition() } - from(Lockscreen, to = Bouncer) { lockscreenToBouncerTransition() } - from(Lockscreen, to = Communal) { lockscreenToCommunalTransition() } - from(Lockscreen, to = Shade) { lockscreenToShadeTransition() } + from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() } + from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() } + from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() } + from(Scenes.Lockscreen, to = Scenes.Shade) { lockscreenToShadeTransition() } from( - Lockscreen, - to = Shade, - key = CollapseShadeInstantly.asComposeAware(), + Scenes.Lockscreen, + to = Scenes.Shade, + key = CollapseShadeInstantly, ) { lockscreenToShadeTransition(durationScale = 0.0) } from( - Lockscreen, - to = Shade, - key = SlightlyFasterShadeCollapse.asComposeAware(), + Scenes.Lockscreen, + to = Scenes.Shade, + key = SlightlyFasterShadeCollapse, ) { lockscreenToShadeTransition(durationScale = 0.9) } - from(Lockscreen, to = QuickSettings) { lockscreenToQuickSettingsTransition() } - from(Lockscreen, to = Gone) { lockscreenToGoneTransition() } - from(Shade, to = QuickSettings) { shadeToQuickSettingsTransition() } + from(Scenes.Lockscreen, to = Scenes.QuickSettings) { lockscreenToQuickSettingsTransition() } + from(Scenes.Lockscreen, to = Scenes.Gone) { lockscreenToGoneTransition() } + from(Scenes.Shade, to = Scenes.QuickSettings) { shadeToQuickSettingsTransition() } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt index 60c0b7719a25..a54994df3dc9 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt @@ -20,10 +20,10 @@ package com.android.systemui.scene.ui.composable import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.scene.shared.model.SceneDataSource -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.TransitionKey import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted @@ -61,11 +61,10 @@ class SceneTransitionLayoutDataSource( } } } - .map { it.asComposeUnaware() } .stateIn( scope = coroutineScope, started = SharingStarted.WhileSubscribed(), - initialValue = state.transitionState.currentScene.asComposeUnaware(), + initialValue = state.transitionState.currentScene, ) override fun changeScene( @@ -73,8 +72,8 @@ class SceneTransitionLayoutDataSource( transitionKey: TransitionKey?, ) { state.setTargetScene( - targetScene = toScene.asComposeAware(), - transitionKey = transitionKey?.asComposeAware(), + targetScene = toScene, + transitionKey = transitionKey, coroutineScope = coroutineScope, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt deleted file mode 100644 index 5a9add1ad587..000000000000 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.android.systemui.scene.ui.composable - -import com.android.systemui.scene.shared.model.SceneKey - -val Lockscreen = SceneKey.Lockscreen.asComposeAware() -val Bouncer = SceneKey.Bouncer.asComposeAware() -val Shade = SceneKey.Shade.asComposeAware() -val QuickSettings = SceneKey.QuickSettings.asComposeAware() -val Gone = SceneKey.Gone.asComposeAware() -val Communal = SceneKey.Communal.asComposeAware() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt index 1a9facea7518..5eefe490ab5b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt @@ -2,10 +2,10 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween import com.android.compose.animation.scene.TransitionBuilder -import com.android.systemui.scene.ui.composable.Bouncer +import com.android.systemui.scene.shared.model.Scenes fun TransitionBuilder.bouncerToGoneTransition() { spec = tween(durationMillis = 500) - fade(Bouncer.rootElementKey) + fade(Scenes.Bouncer.rootElementKey) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt index 291617f8edde..5bd158349f5e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt @@ -3,10 +3,10 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TransitionBuilder -import com.android.systemui.scene.ui.composable.QuickSettings +import com.android.systemui.scene.shared.model.Scenes fun TransitionBuilder.goneToQuickSettingsTransition() { spec = tween(durationMillis = 500) - translate(QuickSettings.rootElementKey, Edge.Top, true) + translate(Scenes.QuickSettings.rootElementKey, Edge.Top, true) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt index ea8110ad8518..0021bf59d875 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt @@ -19,15 +19,14 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TransitionBuilder -import com.android.systemui.scene.ui.composable.Communal -import com.android.systemui.scene.ui.composable.Lockscreen +import com.android.systemui.scene.shared.model.Scenes fun TransitionBuilder.lockscreenToCommunalTransition() { spec = tween(durationMillis = 500) // Translate lockscreen to the left. - translate(Lockscreen.rootElementKey, Edge.Left) + translate(Scenes.Lockscreen.rootElementKey, Edge.Left) // Translate communal from the right. - translate(Communal.rootElementKey, Edge.Right) + translate(Scenes.Communal.rootElementKey, Edge.Right) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt index da6306dc656d..3e576bc9d538 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt @@ -2,10 +2,10 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween import com.android.compose.animation.scene.TransitionBuilder -import com.android.systemui.scene.ui.composable.Lockscreen +import com.android.systemui.scene.shared.model.Scenes fun TransitionBuilder.lockscreenToGoneTransition() { spec = tween(durationMillis = 500) - fade(Lockscreen.rootElementKey) + fade(Scenes.Lockscreen.rootElementKey) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt index e63bc4e458eb..962d8227a016 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt @@ -3,10 +3,10 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TransitionBuilder -import com.android.systemui.scene.ui.composable.QuickSettings +import com.android.systemui.scene.shared.model.Scenes fun TransitionBuilder.lockscreenToQuickSettingsTransition() { spec = tween(durationMillis = 500) - translate(QuickSettings.rootElementKey, Edge.Top, true) + translate(Scenes.QuickSettings.rootElementKey, Edge.Top, true) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index d7911eac8a61..12b07a3a69c2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -63,8 +63,7 @@ import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.res.R -import com.android.systemui.scene.ui.composable.QuickSettings -import com.android.systemui.scene.ui.composable.Shade as ShadeKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel @@ -443,7 +442,7 @@ private fun SceneScope.StatusIcons( }, update = { iconContainer -> iconContainer.setQsExpansionTransitioning( - layoutState.isTransitioningBetween(ShadeKey, QuickSettings) + layoutState.isTransitioningBetween(Scenes.Shade, Scenes.QuickSettings) ) if (isSingleCarrier || !useExpandedFormat) { iconContainer.removeIgnoredSlots(carrierIconSlots) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 8484b7f5273f..3620cc570c66 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -41,7 +41,12 @@ import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.LowestZIndexScenePicker +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.modifiers.thenIf import com.android.systemui.battery.BatteryMeterViewController @@ -56,10 +61,7 @@ import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL import com.android.systemui.notifications.ui.composable.NotificationScrollingStack import com.android.systemui.qs.ui.composable.QuickSettings import com.android.systemui.res.R -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel import com.android.systemui.statusbar.phone.StatusBarIconController @@ -109,7 +111,7 @@ constructor( private val mediaCarouselController: MediaCarouselController, @Named(QUICK_QS_PANEL) private val mediaHost: MediaHost, ) : ComposableScene { - override val key = SceneKey.Shade + override val key = Scenes.Shade override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = viewModel.upDestinationSceneKey @@ -144,8 +146,8 @@ constructor( up: SceneKey, ): Map<UserAction, UserActionResult> { return mapOf( - UserAction.Swipe(Direction.UP) to UserActionResult(up), - UserAction.Swipe(Direction.DOWN) to UserActionResult(SceneKey.QuickSettings), + Swipe(SwipeDirection.Up) to UserActionResult(up), + Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings), ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt index b3fcc305e6b5..53de5bc3a36d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt @@ -27,12 +27,14 @@ import androidx.compose.animation.scaleOut import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -137,12 +139,14 @@ constructor( } } ) { targetViewModel -> - Expandable( - modifier = Modifier.fillMaxSize(), - color = targetViewModel.backgroundColor.toColor(), - shape = RoundedCornerShape(12.dp), - onClick = { viewModel.onDeviceClick(it) }, - ) {} + Spacer( + modifier = + Modifier.fillMaxSize() + .background( + color = targetViewModel.backgroundColor.toColor(), + shape = RoundedCornerShape(12.dp), + ), + ) } transition.AnimatedContent( contentKey = { it.icon }, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 1e3842a1de68..b7e2dd13f321 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -391,7 +391,7 @@ interface SwipeSourceDetector { } /** The result of performing a [UserAction]. */ -class UserActionResult( +data class UserActionResult( /** The scene we should be transitioning to during the [UserAction]. */ val toScene: SceneKey, diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 38dc24ed2f5f..9dbeeda42986 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -31,6 +31,7 @@ import android.view.WindowInsetsController import android.widget.FrameLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger import com.android.internal.widget.LockPatternUtils @@ -65,8 +66,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags import com.android.systemui.scene.shared.model.FakeSceneDataSource -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR import com.android.systemui.statusbar.policy.ConfigurationController @@ -244,7 +244,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { sceneInteractor = kosmos.sceneInteractor keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor sceneTransitionStateFlow = - MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen)) + MutableStateFlow(ObservableTransitionState.Idle(Scenes.Lockscreen)) sceneInteractor.setTransitionState(sceneTransitionStateFlow) deviceEntryInteractor = kosmos.deviceEntryInteractor @@ -815,18 +815,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // not enough to trigger a dismissal of the keyguard. underTest.onViewAttached() fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Bouncer, "reason") + sceneInteractor.changeScene(Scenes.Bouncer, "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( - SceneKey.Lockscreen, - SceneKey.Bouncer, + Scenes.Lockscreen, + Scenes.Bouncer, flowOf(.5f), false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Bouncer) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer) + fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer) + sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyInt()) @@ -835,18 +835,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( - SceneKey.Bouncer, - SceneKey.Gone, + Scenes.Bouncer, + Scenes.Gone, flowOf(.5f), false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) runCurrent() verify(viewMediatorCallback).keyguardDone(anyInt()) @@ -854,18 +854,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // again. clearInvocations(viewMediatorCallback) fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Bouncer, "reason") + sceneInteractor.changeScene(Scenes.Bouncer, "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( - SceneKey.Gone, - SceneKey.Bouncer, + Scenes.Gone, + Scenes.Bouncer, flowOf(.5f), false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Bouncer) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer) + fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer) + sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyInt()) @@ -874,35 +874,35 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // does not dismiss the keyguard while we're not listening. underTest.onViewDetached() fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( - SceneKey.Bouncer, - SceneKey.Gone, + Scenes.Bouncer, + Scenes.Gone, flowOf(.5f), false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyInt()) // While not listening, moving to the lockscreen does not dismiss the keyguard. fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Lockscreen, "reason") + sceneInteractor.changeScene(Scenes.Lockscreen, "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( - SceneKey.Gone, - SceneKey.Lockscreen, + Scenes.Gone, + Scenes.Lockscreen, flowOf(.5f), false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Lockscreen) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) + fakeSceneDataSource.unpause(expectedScene = Scenes.Lockscreen) + sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Lockscreen) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyInt()) @@ -910,18 +910,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // gone scene now does dismiss the keyguard again, this time from lockscreen. underTest.onViewAttached() fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( - SceneKey.Lockscreen, - SceneKey.Gone, + Scenes.Lockscreen, + Scenes.Gone, flowOf(.5f), false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) runCurrent() verify(viewMediatorCallback).keyguardDone(anyInt()) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index ad29e68f1bbf..df50eb64f8b6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.authenticationInteractor @@ -33,7 +34,7 @@ import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus @@ -93,7 +94,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD) assertThat(password).isEmpty() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Password) } @@ -125,7 +126,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(message?.text).isEmpty() assertThat(password).isEqualTo("password") - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -163,7 +164,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Password ) kosmos.fakeDeviceEntryRepository.setUnlocked(false) - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) // No input entered. @@ -209,14 +210,14 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(password).isEqualTo("password") // The user doesn't confirm the password, but navigates back to the lockscreen instead. - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) // The user navigates to the bouncer again. - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) // Ensure the previously-entered password is not shown. assertThat(password).isEmpty() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -330,8 +331,8 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private fun TestScope.switchToScene(toScene: SceneKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) - val bouncerShown = currentScene != SceneKey.Bouncer && toScene == SceneKey.Bouncer - val bouncerHidden = currentScene == SceneKey.Bouncer && toScene != SceneKey.Bouncer + val bouncerShown = currentScene != Scenes.Bouncer && toScene == Scenes.Bouncer + val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer sceneInteractor.changeScene(toScene, "reason") if (bouncerShown) underTest.onShown() if (bouncerHidden) underTest.onHidden() @@ -345,7 +346,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Password ) kosmos.fakeDeviceEntryRepository.setUnlocked(false) - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) } private suspend fun TestScope.setLockout( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 32de1f2a892c..91a056ddd685 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.data.repository.authenticationRepository @@ -31,7 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -86,7 +87,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN) assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pattern) } @@ -104,7 +105,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(message?.text).isEmpty() assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -159,7 +160,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() assertThat(message?.text).isEqualTo(WRONG_PATTERN) - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -369,8 +370,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private fun TestScope.switchToScene(toScene: SceneKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) - val bouncerShown = currentScene != SceneKey.Bouncer && toScene == SceneKey.Bouncer - val bouncerHidden = currentScene == SceneKey.Bouncer && toScene != SceneKey.Bouncer + val bouncerShown = currentScene != Scenes.Bouncer && toScene == Scenes.Bouncer + val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer sceneInteractor.changeScene(toScene, "reason") if (bouncerShown) underTest.onShown() if (bouncerHidden) underTest.onHidden() @@ -384,7 +385,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Pattern ) kosmos.fakeDeviceEntryRepository.setUnlocked(false) - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index ccf7094e2bf7..7b75a3715415 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository @@ -31,7 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -196,7 +197,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(message?.text).isEmpty() assertThat(pin).isEmpty() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -230,7 +231,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(pin).isEmpty() assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -290,7 +291,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(pin).isEmpty() assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -304,10 +305,10 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(pin).isNotEmpty() // The user doesn't confirm the PIN, but navigates back to the lockscreen instead. - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) // The user navigates to the bouncer again. - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) // Ensure the previously-entered PIN is not shown. assertThat(pin).isEmpty() @@ -389,8 +390,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { private fun TestScope.switchToScene(toScene: SceneKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) - val bouncerShown = currentScene != SceneKey.Bouncer && toScene == SceneKey.Bouncer - val bouncerHidden = currentScene == SceneKey.Bouncer && toScene != SceneKey.Bouncer + val bouncerShown = currentScene != Scenes.Bouncer && toScene == Scenes.Bouncer + val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer sceneInteractor.changeScene(toScene, "reason") if (bouncerShown) underTest.onShown() if (bouncerHidden) underTest.onHidden() @@ -402,7 +403,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { private fun TestScope.lockDeviceAndOpenPinBouncer() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) kosmos.fakeDeviceEntryRepository.setUnlocked(false) - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt new file mode 100644 index 000000000000..9cfa57257053 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.os.UserHandle +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.util.settings.fakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@android.platform.test.annotations.EnabledOnRavenwood +class CameraAutoRotateRepositoryImplTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val settings = kosmos.fakeSettings + private val testUser = UserHandle.of(1) + + private val underTest = + CameraAutoRotateRepositoryImpl(settings, testScope.testScheduler, testScope.backgroundScope) + + /** 3 changes => 3 change signals + 1 signal emitted at start => 4 signals */ + @Test + fun isCameraAutoRotateSettingEnabled_3times() = + testScope.runTest { + settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier) + val isCameraAutoRotateSettingEnabled by + collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser)) + runCurrent() + assertThat(isCameraAutoRotateSettingEnabled.last()).isFalse() + + settings.putIntForUser(SETTING_NAME, ENABLE, testUser.identifier) + runCurrent() + assertThat(isCameraAutoRotateSettingEnabled.last()).isTrue() + + settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier) + runCurrent() + assertThat(isCameraAutoRotateSettingEnabled.last()).isFalse() + + settings.putIntForUser(SETTING_NAME, ENABLE, testUser.identifier) + runCurrent() + assertThat(isCameraAutoRotateSettingEnabled.last()).isTrue() + + assertThat(isCameraAutoRotateSettingEnabled).hasSize(4) + } + + @Test + fun isCameraAutoRotateSettingEnabled_emitsOnStart() = + testScope.runTest { + val isCameraAutoRotateSettingEnabled: List<Boolean> by + collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser)) + + runCurrent() + + assertThat(isCameraAutoRotateSettingEnabled).hasSize(1) + } + + /** 0 for 0 changes + 1 signal emitted on start => 1 signal */ + @Test + fun isCameraAutoRotateSettingEnabled_0Times() = + testScope.runTest { + settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier) + val isCameraAutoRotateSettingEnabled: List<Boolean> by + collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser)) + runCurrent() + + settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier) + runCurrent() + + assertThat(isCameraAutoRotateSettingEnabled).hasSize(1) + assertThat(isCameraAutoRotateSettingEnabled[0]).isFalse() + } + + /** Maintain that flows are cached by user */ + @Test + fun sameUserCallsIsCameraAutoRotateSettingEnabledTwice_getsSameFlow() = + testScope.runTest { + val flow1 = underTest.isCameraAutoRotateSettingEnabled(testUser) + val flow2 = underTest.isCameraAutoRotateSettingEnabled(testUser) + + assertThat(flow1).isEqualTo(flow2) + } + + @Test + fun differentUsersCallIsCameraAutoRotateSettingEnabled_getDifferentFlow() = + testScope.runTest { + val user2 = UserHandle.of(2) + val flow1 = underTest.isCameraAutoRotateSettingEnabled(testUser) + val flow2 = underTest.isCameraAutoRotateSettingEnabled(user2) + + assertThat(flow1).isNotEqualTo(flow2) + } + + private companion object { + private const val SETTING_NAME = Settings.Secure.CAMERA_AUTOROTATE + private const val DISABLE = 0 + private const val ENABLE = 1 + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt new file mode 100644 index 000000000000..29de58e2b28f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.hardware.SensorPrivacyManager +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@android.platform.test.annotations.EnabledOnRavenwood +class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val testUser = UserHandle.of(1) + private val privacyManager = mock<SensorPrivacyManager>() + private val underTest = + CameraSensorPrivacyRepositoryImpl( + testScope.testScheduler, + testScope.backgroundScope, + privacyManager + ) + + @Test + fun isEnabled_2TimesForSameUserReturnsCachedFlow() = + testScope.runTest { + val flow1 = underTest.isEnabled(testUser) + val flow2 = underTest.isEnabled(testUser) + runCurrent() + + assertThat(flow1).isEqualTo(flow2) + } + + @Test + fun isEnabled_2TimesForDifferentUsersReturnsTwoDifferentFlows() = + testScope.runTest { + val user2 = UserHandle.of(2) + + val flow1 = underTest.isEnabled(testUser) + val flow2 = underTest.isEnabled(user2) + runCurrent() + + assertThat(flow1).isNotEqualTo(flow2) + } + + @Test + fun isEnabled_dataMatchesSensorPrivacyManager() = + testScope.runTest { + val isEnabled = collectLastValue(underTest.isEnabled(testUser)) + + val captor = + ArgumentCaptor.forClass( + SensorPrivacyManager.OnSensorPrivacyChangedListener::class.java + ) + runCurrent() + assertThat(isEnabled()).isEqualTo(false) + + Mockito.verify(privacyManager) + .addSensorPrivacyListener( + ArgumentMatchers.eq(SensorPrivacyManager.Sensors.CAMERA), + ArgumentMatchers.eq(testUser.identifier), + captor.capture() + ) + val sensorPrivacyCallback = captor.value!! + + sensorPrivacyCallback.onSensorPrivacyChanged(SensorPrivacyManager.Sensors.CAMERA, true) + runCurrent() + assertThat(isEnabled()).isEqualTo(true) + + sensorPrivacyCallback.onSensorPrivacyChanged(SensorPrivacyManager.Sensors.CAMERA, false) + runCurrent() + assertThat(isEnabled()).isEqualTo(false) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt new file mode 100644 index 000000000000..f75e036d78d4 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.Kosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@android.platform.test.annotations.EnabledOnRavenwood +class FakeCameraAutoRotateRepositoryTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val underTest = kosmos.fakeCameraAutoRotateRepository + private val testUser = UserHandle.of(1) + + @Test + fun isCameraAutoRotateSettingEnabled_emitsFalseOnStart() = runTest { + val isCameraAutoRotateSettingEnabled by + collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser)) + + assertThat(isCameraAutoRotateSettingEnabled).hasSize(1) + assertThat(isCameraAutoRotateSettingEnabled.first()).isFalse() + } + + /** + * The value explicitly set in this test is not distinct, therefore only 1 value is collected. + */ + @Test + fun isCameraAutoRotateSettingEnabled_emitsDistinctValueOnly() = runTest { + val isCameraAutoRotateSettingEnabled by + collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser)) + underTest.setEnabled(testUser, false) + runCurrent() + + assertThat(isCameraAutoRotateSettingEnabled).hasSize(1) + assertThat(isCameraAutoRotateSettingEnabled.first()).isFalse() + } + + @Test + fun isCameraAutoRotateSettingEnabled_canSetValue3Times() = runTest { + val isCameraAutoRotateSettingEnabled by + collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser)) + runCurrent() + underTest.setEnabled(testUser, true) + runCurrent() + underTest.setEnabled(testUser, false) + runCurrent() + underTest.setEnabled(testUser, true) + runCurrent() + assertThat(isCameraAutoRotateSettingEnabled).hasSize(4) + assertThat(isCameraAutoRotateSettingEnabled.last()).isTrue() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt new file mode 100644 index 000000000000..7fa1be3d20ff --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.Kosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@android.platform.test.annotations.EnabledOnRavenwood +class FakeCameraSensorPrivacyRepositoryTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val underTest = kosmos.fakeCameraSensorPrivacyRepository + private val testUser = UserHandle.of(1) + + @Test + fun isCameraSensorPrivacyEnabled_emitsFalseOnStart() = runTest { + val isCameraSensorPrivacySettingEnabled by collectValues(underTest.isEnabled(testUser)) + + assertThat(isCameraSensorPrivacySettingEnabled).hasSize(1) + assertThat(isCameraSensorPrivacySettingEnabled.first()).isFalse() + } + + /** + * The value explicitly set in this test is not distinct, therefore only 1 value is collected. + */ + @Test + fun isCameraSensorPrivacyEnabled_emitsDistinctValueOnly() = runTest { + val isCameraSensorPrivacySettingEnabled by collectValues(underTest.isEnabled(testUser)) + underTest.setEnabled(testUser, false) + runCurrent() + + assertThat(isCameraSensorPrivacySettingEnabled).hasSize(1) + assertThat(isCameraSensorPrivacySettingEnabled.first()).isFalse() + } + + @Test + fun isCameraSensorPrivacyEnabled_canSetValue3Times() = runTest { + val isCameraSensorPrivacySettingEnabled by collectValues(underTest.isEnabled(testUser)) + runCurrent() + underTest.setEnabled(testUser, true) + runCurrent() + underTest.setEnabled(testUser, false) + runCurrent() + underTest.setEnabled(testUser, true) + runCurrent() + assertThat(isCameraSensorPrivacySettingEnabled).hasSize(4) + assertThat(isCameraSensorPrivacySettingEnabled.last()).isTrue() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index 92396e0bcdef..ce6445b75fb9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -21,9 +21,8 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.dock.DockManager import com.android.systemui.dock.dockManager import com.android.systemui.dock.fakeDockManager import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -40,6 +39,7 @@ import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @@ -79,8 +79,8 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.PRIMARY_BOUNCER, @@ -88,16 +88,17 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope = this ) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) } } + @Ignore("Ignored until custom animations are implemented in b/322787129") @Test fun deviceDocked_forceCommunalScene() = with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) updateDocked(true) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -105,7 +106,24 @@ class CommunalSceneStartableTest : SysuiTestCase() { to = KeyguardState.LOCKSCREEN, testScope = this ) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) + } + } + + @Test + fun exitingDream_forceCommunalScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + assertThat(scene).isEqualTo(CommunalScenes.Blank) + + updateDocked(true) + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.DREAMING, + to = KeyguardState.LOCKSCREEN, + testScope = this + ) + assertThat(scene).isEqualTo(CommunalScenes.Communal) } } @@ -114,7 +132,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) updateDocked(true) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -122,7 +140,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { to = KeyguardState.LOCKSCREEN, testScope = this ) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) } } @@ -131,19 +149,19 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.OFF, testScope = this ) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) } } @@ -152,17 +170,17 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.OFF, testScope = this ) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY / 2) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.OFF, @@ -171,15 +189,16 @@ class CommunalSceneStartableTest : SysuiTestCase() { ) advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) } } + @Ignore("Ignored until custom animations are implemented in b/322787129") @Test fun dockingOnLockscreen_forcesCommunal() = with(kosmos) { testScope.runTest { - communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + communalInteractor.onSceneChanged(CommunalScenes.Blank) val scene by collectLastValue(communalInteractor.desiredScene) // device is docked while on the lockscreen @@ -190,17 +209,18 @@ class CommunalSceneStartableTest : SysuiTestCase() { ) updateDocked(true) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY) - assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) } } + @Ignore("Ignored until custom animations are implemented in b/322787129") @Test fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() = with(kosmos) { testScope.runTest { - communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + communalInteractor.onSceneChanged(CommunalScenes.Blank) val scene by collectLastValue(communalInteractor.desiredScene) // device is docked while on the lockscreen @@ -211,9 +231,9 @@ class CommunalSceneStartableTest : SysuiTestCase() { ) updateDocked(true) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY / 2) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) // dream starts shortly after docking fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -222,7 +242,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope = this ) advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY) - assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) } } @@ -230,7 +250,8 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { runCurrent() fakeDockManager.setIsDocked(docked) - fakeDockManager.setDockEvent(DockManager.STATE_DOCKED) + // TODO(b/322787129): uncomment once custom animations are in place + // fakeDockManager.setDockEvent(DockManager.STATE_DOCKED) runCurrent() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt index 06b3806cb382..43acf3197fb1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.communal.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.sceneContainerRepository @@ -60,20 +60,17 @@ class CommunalRepositoryImplTest : SysuiTestCase() { testScope.runTest { val transitionState by collectLastValue(underTest.transitionState) assertThat(transitionState) - .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)) + .isEqualTo(ObservableTransitionState.Idle(CommunalScenes.Default)) } @Test fun transitionState_setTransitionState_returnsNewValue() = testScope.runTest { - val expectedSceneKey = CommunalSceneKey.Communal - underTest.setTransitionState( - flowOf(ObservableCommunalTransitionState.Idle(expectedSceneKey)) - ) + val expectedSceneKey = CommunalScenes.Communal + underTest.setTransitionState(flowOf(ObservableTransitionState.Idle(expectedSceneKey))) val transitionState by collectLastValue(underTest.transitionState) - assertThat(transitionState) - .isEqualTo(ObservableCommunalTransitionState.Idle(expectedSceneKey)) + assertThat(transitionState).isEqualTo(ObservableTransitionState.Idle(expectedSceneKey)) } @Test @@ -81,7 +78,7 @@ class CommunalRepositoryImplTest : SysuiTestCase() { testScope.runTest { // Set a value for the transition state flow. underTest.setTransitionState( - flowOf(ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)) + flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) ) // Set the transition state flow back to null. @@ -90,6 +87,6 @@ class CommunalRepositoryImplTest : SysuiTestCase() { // Flow returns default scene key. val transitionState by collectLastValue(underTest.transitionState) assertThat(transitionState) - .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)) + .isEqualTo(ObservableTransitionState.Idle(CommunalScenes.Default)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 6e3573b64f9a..eafd5038759c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -25,6 +25,7 @@ import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl @@ -40,9 +41,8 @@ import com.android.systemui.communal.data.repository.fakeCommunalTutorialReposit import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalContentSize -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags @@ -53,7 +53,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.fakeUserTracker import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository @@ -462,9 +462,9 @@ class CommunalInteractorTest : SysuiTestCase() { var desiredScene = collectLastValue(underTest.desiredScene) runCurrent() - assertThat(desiredScene()).isEqualTo(CommunalSceneKey.Blank) + assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank) - val targetScene = CommunalSceneKey.Communal + val targetScene = CommunalScenes.Communal communalRepository.setDesiredScene(targetScene) desiredScene = collectLastValue(underTest.desiredScene) runCurrent() @@ -474,7 +474,7 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun updatesScene() = testScope.runTest { - val targetScene = CommunalSceneKey.Communal + val targetScene = CommunalScenes.Communal underTest.onSceneChanged(targetScene) @@ -491,32 +491,32 @@ class CommunalInteractorTest : SysuiTestCase() { val desiredScene by collectLastValue(underTest.desiredScene) - underTest.onSceneChanged(CommunalSceneKey.Communal) - assertThat(desiredScene).isEqualTo(CommunalSceneKey.Communal) + underTest.onSceneChanged(CommunalScenes.Communal) + assertThat(desiredScene).isEqualTo(CommunalScenes.Communal) kosmos.setCommunalAvailable(false) runCurrent() // Scene returns blank when communal is not available. - assertThat(desiredScene).isEqualTo(CommunalSceneKey.Blank) + assertThat(desiredScene).isEqualTo(CommunalScenes.Blank) kosmos.setCommunalAvailable(true) runCurrent() // After re-enabling, scene goes back to Communal. - assertThat(desiredScene).isEqualTo(CommunalSceneKey.Communal) + assertThat(desiredScene).isEqualTo(CommunalScenes.Communal) } @Test fun transitionProgress_onTargetScene_fullProgress() = testScope.runTest { - val targetScene = CommunalSceneKey.Blank + val targetScene = CommunalScenes.Blank val transitionProgressFlow = underTest.transitionProgressToScene(targetScene) val transitionProgress by collectLastValue(transitionProgressFlow) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(targetScene) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(targetScene) ) underTest.setTransitionState(transitionState) @@ -527,14 +527,14 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun transitionProgress_notOnTargetScene_noProgress() = testScope.runTest { - val targetScene = CommunalSceneKey.Blank - val currentScene = CommunalSceneKey.Communal + val targetScene = CommunalScenes.Blank + val currentScene = CommunalScenes.Communal val transitionProgressFlow = underTest.transitionProgressToScene(targetScene) val transitionProgress by collectLastValue(transitionProgressFlow) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(currentScene) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(currentScene) ) underTest.setTransitionState(transitionState) @@ -545,14 +545,14 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun transitionProgress_transitioningToTrackedScene() = testScope.runTest { - val currentScene = CommunalSceneKey.Communal - val targetScene = CommunalSceneKey.Blank + val currentScene = CommunalScenes.Communal + val targetScene = CommunalScenes.Blank val transitionProgressFlow = underTest.transitionProgressToScene(targetScene) val transitionProgress by collectLastValue(transitionProgressFlow) var transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(currentScene) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(currentScene) ) underTest.setTransitionState(transitionState) @@ -562,7 +562,7 @@ class CommunalInteractorTest : SysuiTestCase() { val progress = MutableStateFlow(0f) transitionState = MutableStateFlow( - ObservableCommunalTransitionState.Transition( + ObservableTransitionState.Transition( fromScene = currentScene, toScene = targetScene, progress = progress, @@ -581,7 +581,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Transition(1f)) // Transition finishes. - transitionState = MutableStateFlow(ObservableCommunalTransitionState.Idle(targetScene)) + transitionState = MutableStateFlow(ObservableTransitionState.Idle(targetScene)) underTest.setTransitionState(transitionState) assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene)) } @@ -589,14 +589,14 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun transitionProgress_transitioningAwayFromTrackedScene() = testScope.runTest { - val currentScene = CommunalSceneKey.Blank - val targetScene = CommunalSceneKey.Communal + val currentScene = CommunalScenes.Blank + val targetScene = CommunalScenes.Communal val transitionProgressFlow = underTest.transitionProgressToScene(currentScene) val transitionProgress by collectLastValue(transitionProgressFlow) var transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(currentScene) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(currentScene) ) underTest.setTransitionState(transitionState) @@ -606,7 +606,7 @@ class CommunalInteractorTest : SysuiTestCase() { val progress = MutableStateFlow(0f) transitionState = MutableStateFlow( - ObservableCommunalTransitionState.Transition( + ObservableTransitionState.Transition( fromScene = currentScene, toScene = targetScene, progress = progress, @@ -627,7 +627,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.OtherTransition) // Transition finishes. - transitionState = MutableStateFlow(ObservableCommunalTransitionState.Idle(targetScene)) + transitionState = MutableStateFlow(ObservableTransitionState.Idle(targetScene)) underTest.setTransitionState(transitionState) assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene)) } @@ -642,7 +642,7 @@ class CommunalInteractorTest : SysuiTestCase() { runCurrent() assertThat(isCommunalShowing()).isEqualTo(false) - underTest.onSceneChanged(CommunalSceneKey.Communal) + underTest.onSceneChanged(CommunalScenes.Communal) isCommunalShowing = collectLastValue(underTest.isCommunalShowing) runCurrent() @@ -661,17 +661,17 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalShowing).isFalse() // Verify scene changes with the flag doesn't have any impact - sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "") + sceneInteractor.changeScene(Scenes.Communal, loggingReason = "") runCurrent() assertThat(isCommunalShowing).isFalse() // Verify scene changes (without the flag) to communal sets the value to true - underTest.onSceneChanged(CommunalSceneKey.Communal) + underTest.onSceneChanged(CommunalScenes.Communal) runCurrent() assertThat(isCommunalShowing).isTrue() // Verify scene changes (without the flag) to blank sets the value back to false - underTest.onSceneChanged(CommunalSceneKey.Blank) + underTest.onSceneChanged(CommunalScenes.Blank) runCurrent() assertThat(isCommunalShowing).isFalse() } @@ -687,17 +687,17 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalShowing).isFalse() // Verify scene changes without the flag doesn't have any impact - underTest.onSceneChanged(CommunalSceneKey.Communal) + underTest.onSceneChanged(CommunalScenes.Communal) runCurrent() assertThat(isCommunalShowing).isFalse() // Verify scene changes (with the flag) to communal sets the value to true - sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "") + sceneInteractor.changeScene(Scenes.Communal, loggingReason = "") runCurrent() assertThat(isCommunalShowing).isTrue() // Verify scene changes (with the flag) to lockscreen sets the value to false - sceneInteractor.changeScene(SceneKey.Lockscreen, loggingReason = "") + sceneInteractor.changeScene(Scenes.Lockscreen, loggingReason = "") runCurrent() assertThat(isCommunalShowing).isFalse() } @@ -706,8 +706,8 @@ class CommunalInteractorTest : SysuiTestCase() { fun isIdleOnCommunal() = testScope.runTest { val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Blank) ) communalRepository.setTransitionState(transitionState) @@ -717,8 +717,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isIdleOnCommunal).isEqualTo(false) // Transition to communal. - transitionState.value = - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal) runCurrent() // isIdleOnCommunal is now true since we're on communal. @@ -726,9 +725,9 @@ class CommunalInteractorTest : SysuiTestCase() { // Start transition away from communal. transitionState.value = - ObservableCommunalTransitionState.Transition( - fromScene = CommunalSceneKey.Communal, - toScene = CommunalSceneKey.Blank, + ObservableTransitionState.Transition( + fromScene = CommunalScenes.Communal, + toScene = CommunalScenes.Blank, progress = flowOf(0f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -743,8 +742,8 @@ class CommunalInteractorTest : SysuiTestCase() { fun isCommunalVisible() = testScope.runTest { val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Blank) ) communalRepository.setTransitionState(transitionState) @@ -754,9 +753,9 @@ class CommunalInteractorTest : SysuiTestCase() { // Start transition to communal. transitionState.value = - ObservableCommunalTransitionState.Transition( - fromScene = CommunalSceneKey.Blank, - toScene = CommunalSceneKey.Communal, + ObservableTransitionState.Transition( + fromScene = CommunalScenes.Blank, + toScene = CommunalScenes.Communal, progress = flowOf(0f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -766,17 +765,16 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalVisible).isEqualTo(true) // Finish transition to communal - transitionState.value = - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal) // isCommunalVisible is true since we're on communal. assertThat(isCommunalVisible).isEqualTo(true) // Start transition away from communal. transitionState.value = - ObservableCommunalTransitionState.Transition( - fromScene = CommunalSceneKey.Communal, - toScene = CommunalSceneKey.Blank, + ObservableTransitionState.Transition( + fromScene = CommunalScenes.Communal, + toScene = CommunalScenes.Blank, progress = flowOf(1.0f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt index 8b785927ba5e..50b8da62b3f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt @@ -25,7 +25,7 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -158,7 +158,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { kosmos.setCommunalAvailable(true) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) - communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + communalInteractor.onSceneChanged(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED) } @@ -171,7 +171,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { goToCommunal() communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED) - communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + communalInteractor.onSceneChanged(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED) } @@ -184,13 +184,13 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { goToCommunal() communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) - communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + communalInteractor.onSceneChanged(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED) } private suspend fun goToCommunal() { kosmos.setCommunalAvailable(true) - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt index 6b1b93777fbc..a51315bd96b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt @@ -18,13 +18,14 @@ package com.android.systemui.communal.log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.shared.log.CommunalUiEvent -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -73,7 +74,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() { testScope.runTest { // Transition state is default (non-communal) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT)) + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default)) communalInteractor.setTransitionState(transitionState) runCurrent() @@ -81,14 +82,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() { verify(uiEventLogger, never()).log(any()) // Start transition to communal - transitionState.value = transition(to = CommunalSceneKey.Communal) + transitionState.value = transition(to = CommunalScenes.Communal) runCurrent() // Verify UiEvent logged verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START) // Finish transition to communal - transitionState.value = idle(CommunalSceneKey.Communal) + transitionState.value = idle(CommunalScenes.Communal) runCurrent() // Verify UiEvent logged @@ -101,7 +102,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() { testScope.runTest { // Transition state is default (non-communal) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT)) + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default)) communalInteractor.setTransitionState(transitionState) runCurrent() @@ -109,14 +110,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() { verify(uiEventLogger, never()).log(any()) // Start transition to communal - transitionState.value = transition(to = CommunalSceneKey.Communal) + transitionState.value = transition(to = CommunalScenes.Communal) runCurrent() // Verify UiEvent logged verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START) // Cancel the transition - transitionState.value = idle(CommunalSceneKey.DEFAULT) + transitionState.value = idle(CommunalScenes.Default) runCurrent() // Verify UiEvent logged @@ -132,7 +133,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() { testScope.runTest { // Transition state is communal val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal)) + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal)) communalInteractor.setTransitionState(transitionState) runCurrent() @@ -140,14 +141,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() { verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) // Start transition from communal - transitionState.value = transition(from = CommunalSceneKey.Communal) + transitionState.value = transition(from = CommunalScenes.Communal) runCurrent() // Verify UiEvent logged verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START) // Finish transition to communal - transitionState.value = idle(CommunalSceneKey.DEFAULT) + transitionState.value = idle(CommunalScenes.Default) runCurrent() // Verify UiEvent logged @@ -160,7 +161,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() { testScope.runTest { // Transition state is communal val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal)) + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal)) communalInteractor.setTransitionState(transitionState) runCurrent() @@ -168,14 +169,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() { clearInvocations(uiEventLogger) // Start transition from communal - transitionState.value = transition(from = CommunalSceneKey.Communal) + transitionState.value = transition(from = CommunalScenes.Communal) runCurrent() // Verify UiEvent logged verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START) // Cancel the transition - transitionState.value = idle(CommunalSceneKey.Communal) + transitionState.value = idle(CommunalScenes.Communal) runCurrent() // Verify UiEvent logged @@ -187,10 +188,10 @@ class CommunalLoggerStartableTest : SysuiTestCase() { } private fun transition( - from: CommunalSceneKey = CommunalSceneKey.DEFAULT, - to: CommunalSceneKey = CommunalSceneKey.DEFAULT, - ): ObservableCommunalTransitionState.Transition { - return ObservableCommunalTransitionState.Transition( + from: SceneKey = CommunalScenes.Default, + to: SceneKey = CommunalScenes.Default, + ): ObservableTransitionState.Transition { + return ObservableTransitionState.Transition( fromScene = from, toScene = to, progress = emptyFlow(), @@ -199,7 +200,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() { ) } - private fun idle(sceneKey: CommunalSceneKey): ObservableCommunalTransitionState.Idle { - return ObservableCommunalTransitionState.Idle(sceneKey) + private fun idle(sceneKey: SceneKey): ObservableTransitionState.Idle { + return ObservableTransitionState.Idle(sceneKey) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 563aad1920f7..8f802b80781f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -113,6 +114,7 @@ class CommunalViewModelTest : SysuiTestCase() { kosmos.communalInteractor, kosmos.communalTutorialInteractor, kosmos.shadeInteractor, + kosmos.deviceEntryInteractor, mediaHost, logcatLogBuffer("CommunalViewModelTest"), ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index 98719dd32e5a..4f44705b7e72 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.deviceentry.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository @@ -31,7 +32,7 @@ import com.android.systemui.keyguard.data.repository.fakeTrustRepository import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -120,7 +121,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { testScope.runTest { val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) setupSwipeDeviceEntryMethod() - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) assertThat(isDeviceEntered).isFalse() } @@ -130,9 +131,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() { testScope.runTest { val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) setupSwipeDeviceEntryMethod() - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) runCurrent() - switchToScene(SceneKey.Shade) + switchToScene(Scenes.Shade) assertThat(isDeviceEntered).isFalse() } @@ -142,9 +143,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() { testScope.runTest { val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) setupSwipeDeviceEntryMethod() - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) runCurrent() - switchToScene(SceneKey.Gone) + switchToScene(Scenes.Gone) assertThat(isDeviceEntered).isTrue() } @@ -154,11 +155,11 @@ class DeviceEntryInteractorTest : SysuiTestCase() { testScope.runTest { val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) setupSwipeDeviceEntryMethod() - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) runCurrent() - switchToScene(SceneKey.Gone) + switchToScene(Scenes.Gone) runCurrent() - switchToScene(SceneKey.Shade) + switchToScene(Scenes.Shade) assertThat(isDeviceEntered).isTrue() } @@ -170,9 +171,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() { AuthenticationMethodModel.Pattern ) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) runCurrent() - switchToScene(SceneKey.Bouncer) + switchToScene(Scenes.Bouncer) val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) assertThat(isDeviceEntered).isFalse() @@ -182,7 +183,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun canSwipeToEnter_onLockscreenWithSwipe_isTrue() = testScope.runTest { setupSwipeDeviceEntryMethod() - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter) assertThat(canSwipeToEnter).isTrue() @@ -195,7 +196,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { AuthenticationMethodModel.Pin ) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter) assertThat(canSwipeToEnter).isFalse() @@ -205,9 +206,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun canSwipeToEnter_afterLockscreenDismissedInSwipeMode_isFalse() = testScope.runTest { setupSwipeDeviceEntryMethod() - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) runCurrent() - switchToScene(SceneKey.Gone) + switchToScene(Scenes.Gone) val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter) assertThat(canSwipeToEnter).isFalse() @@ -225,7 +226,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) assertThat(canSwipeToEnter).isFalse() trustRepository.setCurrentUserTrusted(true) @@ -242,7 +243,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) - switchToScene(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) assertThat(canSwipeToEnter).isFalse() faceAuthRepository.isAuthenticated.value = true @@ -311,8 +312,8 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun showOrUnlockDevice_notLocked_switchesToGoneScene() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) - switchToScene(SceneKey.Lockscreen) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin @@ -322,15 +323,15 @@ class DeviceEntryInteractorTest : SysuiTestCase() { underTest.attemptDeviceEntry() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) } @Test fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) - switchToScene(SceneKey.Lockscreen) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None @@ -339,15 +340,15 @@ class DeviceEntryInteractorTest : SysuiTestCase() { underTest.attemptDeviceEntry() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) } @Test fun showOrUnlockDevice_authMethodSwipe_switchesToGoneScene() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) - switchToScene(SceneKey.Lockscreen) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + switchToScene(Scenes.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( @@ -357,7 +358,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { underTest.attemptDeviceEntry() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 19b80da62dc7..128b46533a8b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository +import com.android.systemui.common.shared.model.Position import com.android.systemui.coroutines.collectLastValue import com.android.systemui.doze.DozeMachine import com.android.systemui.doze.DozeTransitionCallback @@ -151,6 +152,24 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test + fun clockPosition() = + testScope.runTest { + assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0)) + + underTest.setClockPosition(0, 1) + assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1)) + + underTest.setClockPosition(1, 9) + assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9)) + + underTest.setClockPosition(1, 0) + assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0)) + + underTest.setClockPosition(3, 1) + assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1)) + } + + @Test fun dozeTimeTick() = testScope.runTest { val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index ef2b6f0805d6..f9ec3d161bb0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository @@ -35,8 +36,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -64,7 +64,7 @@ class KeyguardInteractorTest : SysuiTestCase() { private val shadeRepository = FakeShadeRepository() private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val transitionState: MutableStateFlow<ObservableTransitionState> = - MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Gone)) + MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone)) private val underTest by lazy { KeyguardInteractor( @@ -250,8 +250,8 @@ class KeyguardInteractorTest : SysuiTestCase() { underTest.setAnimateDozingTransitions(true) transitionState.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Lockscreen, + fromScene = Scenes.Gone, + toScene = Scenes.Lockscreen, progress = flowOf(0f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt index 225b5b1408e3..b0f59fe68f11 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt @@ -71,7 +71,7 @@ class AodBurnInViewModelTest : SysuiTestCase() { mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) MockitoAnnotations.initMocks(this) - whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow) + whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow) kosmos.burnInInteractor = burnInInteractor whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt())) .thenReturn(emptyFlow()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 8e15b5d5657f..979d50463a04 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -22,12 +22,12 @@ package com.android.systemui.keyguard.ui.viewmodel import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags as AConfigFlags import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.communalRepository -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.flags.Flags @@ -262,7 +262,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() { // Hub transition state is idle with hub open. communalRepository.setTransitionState( - flowOf(ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)) + flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) ) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 3104842a9c2a..9ff76be30f79 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -32,7 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock @@ -62,9 +62,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { ) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeDeviceEntryRepository.setUnlocked(true) - sceneInteractor.changeScene(SceneKey.Lockscreen, "reason") + sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) + assertThat(upTransitionSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -75,9 +75,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Pin ) kosmos.fakeDeviceEntryRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneKey.Lockscreen, "reason") + sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(upTransitionSceneKey).isEqualTo(Scenes.Bouncer) } @EnableFlags(FLAG_COMMUNAL_HUB) @@ -89,7 +89,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { kosmos.setCommunalAvailable(true) runCurrent() - assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal) + assertThat(leftDestinationSceneKey).isEqualTo(Scenes.Communal) } private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt new file mode 100644 index 000000000000..266875e9e12a --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation.domain.interactor + +import android.Manifest +import android.content.packageManager +import android.content.pm.PackageManager +import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood +import android.testing.LeakCheck +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepository +import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.android.systemui.utils.leaks.FakeBatteryController +import com.android.systemui.utils.leaks.FakeRotationLockController +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toCollection +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class RotationLockTileDataInteractorTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val batteryController = FakeBatteryController(LeakCheck()) + private val rotationController = FakeRotationLockController(LeakCheck()) + private val fakeCameraAutoRotateRepository = kosmos.fakeCameraAutoRotateRepository + private val fakeCameraSensorPrivacyRepository = kosmos.fakeCameraSensorPrivacyRepository + private val packageManager = kosmos.packageManager + + private val testUser = UserHandle.of(1) + private lateinit var underTest: RotationLockTileDataInteractor + + @Before + fun setup() { + whenever(packageManager.rotationResolverPackageName).thenReturn(TEST_PACKAGE_NAME) + whenever( + packageManager.checkPermission( + eq(Manifest.permission.CAMERA), + eq(TEST_PACKAGE_NAME) + ) + ) + .thenReturn(PackageManager.PERMISSION_GRANTED) + + underTest = + RotationLockTileDataInteractor( + rotationController, + batteryController, + fakeCameraAutoRotateRepository, + fakeCameraSensorPrivacyRepository, + packageManager, + context.orCreateTestableResources + .apply { + addOverride(com.android.internal.R.bool.config_allowRotationResolver, true) + } + .resources + ) + } + + @Test + fun availability_isTrue() = + testScope.runTest { + val availability = underTest.availability(testUser).toCollection(mutableListOf()) + + assertThat(availability).hasSize(1) + assertThat(availability.last()).isTrue() + } + + @Test + fun tileData_isRotationLockedMatchesRotationController() = + testScope.runTest { + val data by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + + runCurrent() + assertThat(data!!.isRotationLocked).isEqualTo(false) + + rotationController.setRotationLocked(true, CALLER) + runCurrent() + assertThat(data!!.isRotationLocked).isEqualTo(true) + + rotationController.setRotationLocked(false, CALLER) + runCurrent() + assertThat(data!!.isRotationLocked).isEqualTo(false) + } + + @Test + fun tileData_cameraRotationMatchesBatteryController() = + testScope.runTest { + setupControllersToEnableCameraRotation() + val data by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + + runCurrent() + assertThat(data!!.isCameraRotationEnabled).isTrue() + + batteryController.setPowerSaveMode(true) + runCurrent() + assertThat(data!!.isCameraRotationEnabled).isFalse() + + batteryController.setPowerSaveMode(false) + runCurrent() + assertThat(data!!.isCameraRotationEnabled).isTrue() + } + + @Test + fun tileData_cameraRotationMatchesSensorPrivacyRepository() = + testScope.runTest { + setupControllersToEnableCameraRotation() + val lastValue by + this.collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isTrue() + + fakeCameraSensorPrivacyRepository.setEnabled(testUser, true) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isFalse() + + fakeCameraSensorPrivacyRepository.setEnabled(testUser, false) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isTrue() + } + + @Test + fun tileData_cameraRotationMatchesAutoRotateRepository() = + testScope.runTest { + setupControllersToEnableCameraRotation() + + val lastValue by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isTrue() + + fakeCameraAutoRotateRepository.setEnabled(testUser, false) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isFalse() + + fakeCameraAutoRotateRepository.setEnabled(testUser, true) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isTrue() + } + + @Test + fun tileData_matchesPackageManagerPermissionDenied() = + testScope.runTest { + whenever( + packageManager.checkPermission( + eq(Manifest.permission.CAMERA), + eq(TEST_PACKAGE_NAME) + ) + ) + .thenReturn(PackageManager.PERMISSION_DENIED) + + val lastValue by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(lastValue!!.isCameraRotationEnabled).isEqualTo(false) + } + + @Test + fun tileData_setConfigAllowRotationResolverToFalse_cameraRotationIsNotEnabled() = + testScope.runTest { + underTest.apply { + overrideResource(com.android.internal.R.bool.config_allowRotationResolver, false) + } + setupControllersToEnableCameraRotation() + val lastValue by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + + assertThat(lastValue!!.isCameraRotationEnabled).isEqualTo(false) + } + + private fun setupControllersToEnableCameraRotation() { + rotationController.setRotationLocked(true, CALLER) + batteryController.setPowerSaveMode(false) + fakeCameraSensorPrivacyRepository.setEnabled(testUser, false) + fakeCameraAutoRotateRepository.setEnabled(testUser, true) + } + + private companion object { + private const val CALLER = "RotationLockTileDataInteractorTest" + private const val TEST_PACKAGE_NAME = "com.test" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..1653ce369ea1 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation.domain.interactor + +import android.platform.test.annotations.EnabledOnRavenwood +import android.provider.Settings +import android.testing.LeakCheck +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel +import com.android.systemui.utils.leaks.FakeRotationLockController +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class RotationLockTileUserActionInteractorTest : SysuiTestCase() { + private val controller = FakeRotationLockController(LeakCheck()) + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private val underTest = + RotationLockTileUserActionInteractor( + controller, + inputHandler, + ) + + @Test + fun handleClickWhenEnabled() = runTest { + val wasEnabled = true + controller.setRotationLocked(wasEnabled, null) + + underTest.handleInput(QSTileInputTestKtx.click(RotationLockTileModel(wasEnabled, false))) + + assertThat(controller.isRotationLocked).isEqualTo(!wasEnabled) + } + + @Test + fun handleClickWhenDisabled() = runTest { + val wasEnabled = false + controller.setRotationLocked(wasEnabled, null) + + underTest.handleInput(QSTileInputTestKtx.click(RotationLockTileModel(wasEnabled, false))) + + assertThat(controller.isRotationLocked).isEqualTo(!wasEnabled) + } + + @Test + fun handleLongClickWhenDisabled() = runTest { + val enabled = false + + underTest.handleInput( + QSTileInputTestKtx.longClick( + RotationLockTileModel( + enabled, + false, + ) + ) + ) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTO_ROTATE_SETTINGS) + } + } + + @Test + fun handleLongClickWhenEnabled() = runTest { + val enabled = true + + underTest.handleInput(QSTileInputTestKtx.longClick(RotationLockTileModel(enabled, false))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTO_ROTATE_SETTINGS) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt new file mode 100644 index 000000000000..60c69f427ef3 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation.ui.mapper + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel +import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import com.android.systemui.statusbar.policy.DevicePostureController +import com.android.systemui.statusbar.policy.devicePostureController +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class RotationLockTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig + private val devicePostureController = kosmos.devicePostureController + + private lateinit var mapper: RotationLockTileMapper + + @Before + fun setup() { + whenever(devicePostureController.devicePosture) + .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED) + + mapper = + RotationLockTileMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_auto_rotate_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_auto_rotate_icon_on, TestStubDrawable()) + addOverride(com.android.internal.R.bool.config_allowRotationResolver, true) + addOverride( + com.android.internal.R.array.config_foldedDeviceStates, + intArrayOf() // empty array <=> device is not foldable + ) + } + .resources, + context.theme, + devicePostureController + ) + } + + @Test + fun rotationNotLocked_cameraRotationDisabled() { + val inputModel = RotationLockTileModel(false, false) + + val outputState = mapper.map(rotationLockTileConfig, inputModel) + + val expectedState = + createRotationLockTileState( + QSTileState.ActivationState.ACTIVE, + EMPTY_SECONDARY_STRING, + R.drawable.qs_auto_rotate_icon_on + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun rotationNotLocked_cameraRotationEnabled() { + val inputModel = RotationLockTileModel(false, true) + + val outputState = mapper.map(rotationLockTileConfig, inputModel) + + val expectedState = + createRotationLockTileState( + QSTileState.ActivationState.ACTIVE, + context.getString(R.string.rotation_lock_camera_rotation_on), + R.drawable.qs_auto_rotate_icon_on + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun rotationLocked_cameraRotationNotEnabled() { + val inputModel = RotationLockTileModel(true, false) + + val outputState = mapper.map(rotationLockTileConfig, inputModel) + + val expectedState = + createRotationLockTileState( + QSTileState.ActivationState.INACTIVE, + EMPTY_SECONDARY_STRING, + R.drawable.qs_auto_rotate_icon_off + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun deviceFoldableAndClosed_secondaryLabelIsFoldableSpecific() { + setDeviceFoldable() + val inputModel = RotationLockTileModel(false, false) + + val outputState = mapper.map(rotationLockTileConfig, inputModel) + + val expectedSecondaryLabelEnding = + context.getString(R.string.quick_settings_rotation_posture_folded) + assertThat( + context.resources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates + ) + ) + .isNotEmpty() + val actualSecondaryLabel = outputState.secondaryLabel + assertThat(actualSecondaryLabel).isNotNull() + assertThat(actualSecondaryLabel!!.endsWith(expectedSecondaryLabelEnding)).isTrue() + } + + @Test + fun deviceFoldableAndNotClosed_secondaryLabelIsFoldableSpecific() { + setDeviceFoldable() + whenever(devicePostureController.devicePosture) + .thenReturn(DevicePostureController.DEVICE_POSTURE_OPENED) + val inputModel = RotationLockTileModel(false, false) + + val outputState = mapper.map(rotationLockTileConfig, inputModel) + + val expectedSecondaryLabelEnding = + context.getString(R.string.quick_settings_rotation_posture_unfolded) + assertThat( + context.orCreateTestableResources.resources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates + ) + ) + .isNotEmpty() + val actualSecondaryLabel = outputState.secondaryLabel + assertThat(actualSecondaryLabel).isNotNull() + assertThat(actualSecondaryLabel!!.endsWith(expectedSecondaryLabelEnding)).isTrue() + } + + private fun setDeviceFoldable() { + mapper.apply { + overrideResource( + com.android.internal.R.array.config_foldedDeviceStates, + intArrayOf(1, 2, 3) + ) + } + } + + private fun createRotationLockTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String, + iconRes: Int + ): QSTileState { + val label = context.getString(R.string.quick_settings_rotation_unlocked_label) + return QSTileState( + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + context.getString(R.string.accessibility_quick_settings_rotation), + secondaryLabel, + QSTileState.SideViewIcon.None, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } + + private companion object { + private const val EMPTY_SECONDARY_STRING = "" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 1eb9adb8c004..63f00c1356e8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -18,6 +18,10 @@ package com.android.systemui.qs.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.Back +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlagsClassic @@ -26,10 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.privacyChipInteractor import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel @@ -121,8 +122,8 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { assertThat(destinations) .isEqualTo( mapOf( - UserAction.Back to UserActionResult(SceneKey.Shade), - UserAction.Swipe(Direction.UP) to UserActionResult(SceneKey.Shade), + Back to UserActionResult(Scenes.Shade), + Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade), ) ) } @@ -136,7 +137,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { assertThat(destinations) .isEqualTo( mapOf( - UserAction.Back to UserActionResult(SceneKey.QuickSettings), + Back to UserActionResult(Scenes.QuickSettings), ) ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 667f516317be..a2c4f4e63c19 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -22,6 +22,8 @@ import android.telecom.TelecomManager import android.telephony.TelephonyManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.internal.R import com.android.internal.util.EmergencyAffordanceManager import com.android.internal.util.emergencyAffordanceManager @@ -61,8 +63,7 @@ import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.SceneContainerStartable import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.settings.FakeDisplayTracker @@ -287,19 +288,19 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { } @Test - fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(SceneKey.Lockscreen) } + fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(Scenes.Lockscreen) } @Test fun clickLockButtonAndEnterCorrectPin_unlocksDevice() = testScope.runTest { - emulateUserDrivenTransition(SceneKey.Bouncer) + emulateUserDrivenTransition(Scenes.Bouncer) fakeSceneDataSource.pause() enterPin() emulatePendingTransitionProgress( expectedVisible = false, ) - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) } @Test @@ -307,7 +308,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { val upDestinationSceneKey by collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition( to = upDestinationSceneKey, ) @@ -317,7 +318,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { emulatePendingTransitionProgress( expectedVisible = false, ) - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) } @Test @@ -327,7 +328,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { val upDestinationSceneKey by collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) emulateUserDrivenTransition( to = upDestinationSceneKey, ) @@ -338,13 +339,13 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey) setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true) - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) // Emulate a user swipe to the shade scene. - emulateUserDrivenTransition(to = SceneKey.Shade) - assertCurrentScene(SceneKey.Shade) + emulateUserDrivenTransition(to = Scenes.Shade) + assertCurrentScene(Scenes.Shade) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen) emulateUserDrivenTransition( to = upDestinationSceneKey, ) @@ -356,17 +357,17 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey) setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true) assertThat(deviceEntryInteractor.canSwipeToEnter.value).isTrue() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) // Emulate a user swipe to dismiss the lockscreen. - emulateUserDrivenTransition(to = SceneKey.Gone) - assertCurrentScene(SceneKey.Gone) + emulateUserDrivenTransition(to = Scenes.Gone) + assertCurrentScene(Scenes.Gone) // Emulate a user swipe to the shade scene. - emulateUserDrivenTransition(to = SceneKey.Shade) - assertCurrentScene(SceneKey.Shade) + emulateUserDrivenTransition(to = Scenes.Shade) + assertCurrentScene(Scenes.Shade) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) emulateUserDrivenTransition( to = upDestinationSceneKey, ) @@ -377,10 +378,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false) putDeviceToSleep(instantlyLockDevice = false) - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) wakeUpDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) } @Test @@ -388,45 +389,45 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true) putDeviceToSleep(instantlyLockDevice = false) - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) wakeUpDevice() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) } @Test fun deviceGoesToSleep_switchesToLockscreen() = testScope.runTest { unlockDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) putDeviceToSleep() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) } @Test fun deviceGoesToSleep_wakeUp_unlock() = testScope.runTest { unlockDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) putDeviceToSleep() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) wakeUpDevice() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) unlockDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) } @Test fun deviceWakesUpWhileUnlocked_dismissesLockscreen() = testScope.runTest { unlockDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) putDeviceToSleep(instantlyLockDevice = false) - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) wakeUpDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) } @Test @@ -435,20 +436,20 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { unlockDevice() val upDestinationSceneKey by collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) } @Test fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() = testScope.runTest { unlockDevice() - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) putDeviceToSleep(instantlyLockDevice = false) - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) // Pretend like the timeout elapsed and now lock the device. lockDevice() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) } @Test @@ -457,7 +458,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { setAuthMethod(AuthenticationMethodModel.Password) val upDestinationSceneKey by collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition( to = upDestinationSceneKey, ) @@ -466,7 +467,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { dismissIme() emulatePendingTransitionProgress() - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) } @Test @@ -475,7 +476,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { setAuthMethod(AuthenticationMethodModel.Password) val upDestinationSceneKey by collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition(to = upDestinationSceneKey) val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton) @@ -495,7 +496,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { startPhoneCall() val upDestinationSceneKey by collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) - assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition(to = upDestinationSceneKey) val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton) @@ -513,7 +514,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { setAuthMethod(AuthenticationMethodModel.None) introduceLockedSim() - assertCurrentScene(SceneKey.Bouncer) + assertCurrentScene(Scenes.Bouncer) } @Test @@ -523,7 +524,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { introduceLockedSim() emulatePendingTransitionProgress(expectedVisible = true) enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.None) - assertCurrentScene(SceneKey.Gone) + assertCurrentScene(Scenes.Gone) } @Test @@ -533,7 +534,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { introduceLockedSim() emulatePendingTransitionProgress(expectedVisible = true) enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin) - assertCurrentScene(SceneKey.Lockscreen) + assertCurrentScene(Scenes.Lockscreen) } @Test @@ -657,7 +658,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to) bouncerSceneJob = - if (to == SceneKey.Bouncer) { + if (to == Scenes.Bouncer) { testScope.backgroundScope.launch { bouncerViewModel.authMethodViewModel.collect { // Do nothing. Need this to turn this otherwise cold flow, hot. @@ -688,7 +689,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { sceneInteractor.changeScene(to, "reason") emulatePendingTransitionProgress( - expectedVisible = to != SceneKey.Gone, + expectedVisible = to != Scenes.Gone, ) } @@ -715,7 +716,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { .that(deviceEntryInteractor.isUnlocked.value) .isFalse() - emulateUserDrivenTransition(SceneKey.Bouncer) + emulateUserDrivenTransition(Scenes.Bouncer) fakeSceneDataSource.pause() enterPin() // This repository state is not changed by the AuthInteractor, it relies on @@ -729,7 +730,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { /** * Enters the correct PIN in the bouncer UI. * - * Asserts that the current scene is [SceneKey.Bouncer] and that the current bouncer UI is a PIN + * Asserts that the current scene is [Scenes.Bouncer] and that the current bouncer UI is a PIN * before proceeding. * * Does not assert that the device is locked or unlocked. @@ -737,7 +738,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private fun TestScope.enterPin() { assertWithMessage("Cannot enter PIN when not on the Bouncer scene!") .that(getCurrentSceneInUi()) - .isEqualTo(SceneKey.Bouncer) + .isEqualTo(Scenes.Bouncer) val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel) assertWithMessage("Cannot enter PIN when not using a PIN authentication method!") .that(authMethodViewModel) @@ -754,7 +755,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { /** * Enters the correct PIN in the sim bouncer UI. * - * Asserts that the current scene is [SceneKey.Bouncer] and that the current bouncer UI is a PIN + * Asserts that the current scene is [Scenes.Bouncer] and that the current bouncer UI is a PIN * before proceeding. * * Does not assert that the device is locked or unlocked. @@ -764,7 +765,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { ) { assertWithMessage("Cannot enter PIN when not on the Bouncer scene!") .that(getCurrentSceneInUi()) - .isEqualTo(SceneKey.Bouncer) + .isEqualTo(Scenes.Bouncer) val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel) assertWithMessage("Cannot enter PIN when not using a PIN authentication method!") .that(authMethodViewModel) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index 1da3bc1aeda8..3d6619272dbe 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -20,14 +20,14 @@ package com.android.systemui.scene.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -51,12 +51,12 @@ class SceneContainerRepositoryTest : SysuiTestCase() { assertThat(underTest.allSceneKeys()) .isEqualTo( listOf( - SceneKey.QuickSettings, - SceneKey.Shade, - SceneKey.Lockscreen, - SceneKey.Bouncer, - SceneKey.Gone, - SceneKey.Communal, + Scenes.QuickSettings, + Scenes.Shade, + Scenes.Lockscreen, + Scenes.Bouncer, + Scenes.Gone, + Scenes.Communal, ) ) } @@ -66,17 +66,17 @@ class SceneContainerRepositoryTest : SysuiTestCase() { testScope.runTest { val underTest = kosmos.sceneContainerRepository val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) - underTest.changeScene(SceneKey.Shade) - assertThat(currentScene).isEqualTo(SceneKey.Shade) + underTest.changeScene(Scenes.Shade) + assertThat(currentScene).isEqualTo(Scenes.Shade) } @Test(expected = IllegalStateException::class) fun changeScene_noSuchSceneInContainer_throws() { - kosmos.sceneKeys = listOf(SceneKey.QuickSettings, SceneKey.Lockscreen) + kosmos.sceneKeys = listOf(Scenes.QuickSettings, Scenes.Lockscreen) val underTest = kosmos.sceneContainerRepository - underTest.changeScene(SceneKey.Shade) + underTest.changeScene(Scenes.Shade) } @Test @@ -111,7 +111,7 @@ class SceneContainerRepositoryTest : SysuiTestCase() { val underTest = kosmos.sceneContainerRepository val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Lockscreen) + ObservableTransitionState.Idle(Scenes.Lockscreen) ) underTest.setTransitionState(transitionState) val reflectedTransitionState by collectLastValue(underTest.transitionState) @@ -120,8 +120,8 @@ class SceneContainerRepositoryTest : SysuiTestCase() { val progress = MutableStateFlow(1f) transitionState.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, - toScene = SceneKey.Shade, + fromScene = Scenes.Lockscreen, + toScene = Scenes.Shade, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt index 9b0adb172e8d..6b5997fc21c4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt @@ -21,6 +21,8 @@ package com.android.systemui.scene.domain.interactor import android.platform.test.annotations.DisableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -28,8 +30,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.statusbar.notification.stack.ui.viewmodel.panelExpansionInteractor @@ -56,7 +57,7 @@ class PanelExpansionInteractorTest : SysuiTestCase() { private val sceneInteractor = kosmos.sceneInteractor private val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Lockscreen) + ObservableTransitionState.Idle(Scenes.Lockscreen) ) private val fakeSceneDataSource = kosmos.fakeSceneDataSource private val fakeShadeRepository = kosmos.fakeShadeRepository @@ -76,19 +77,19 @@ class PanelExpansionInteractorTest : SysuiTestCase() { setUnlocked(false) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) - changeScene(SceneKey.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } + changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(SceneKey.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } + changeScene(Scenes.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(SceneKey.Shade) { assertThat(panelExpansion).isEqualTo(1f) } + changeScene(Scenes.Shade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(SceneKey.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) } + changeScene(Scenes.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(SceneKey.Communal) { assertThat(panelExpansion).isEqualTo(1f) } + changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @@ -100,21 +101,19 @@ class PanelExpansionInteractorTest : SysuiTestCase() { setUnlocked(true) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) - changeScene(SceneKey.Gone) { assertThat(panelExpansion).isEqualTo(0f) } + changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) } assertThat(panelExpansion).isEqualTo(0f) - changeScene(SceneKey.Shade) { progress -> - assertThat(panelExpansion).isEqualTo(progress) - } + changeScene(Scenes.Shade) { progress -> assertThat(panelExpansion).isEqualTo(progress) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(SceneKey.QuickSettings) { + changeScene(Scenes.QuickSettings) { // Shade's already expanded, so moving to QS should also be 1f. assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(SceneKey.Communal) { assertThat(panelExpansion).isEqualTo(1f) } + changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @@ -128,19 +127,19 @@ class PanelExpansionInteractorTest : SysuiTestCase() { setUnlocked(false) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) - changeScene(SceneKey.Lockscreen) + changeScene(Scenes.Lockscreen) assertThat(panelExpansion).isEqualTo(leet) - changeScene(SceneKey.Bouncer) + changeScene(Scenes.Bouncer) assertThat(panelExpansion).isEqualTo(leet) - changeScene(SceneKey.Shade) + changeScene(Scenes.Shade) assertThat(panelExpansion).isEqualTo(leet) - changeScene(SceneKey.QuickSettings) + changeScene(Scenes.QuickSettings) assertThat(panelExpansion).isEqualTo(leet) - changeScene(SceneKey.Communal) + changeScene(Scenes.Communal) assertThat(panelExpansion).isEqualTo(leet) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index db94c39e1cb1..f645f1cc4369 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.scene.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository @@ -28,8 +29,7 @@ import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -67,23 +67,23 @@ class SceneInteractorTest : SysuiTestCase() { fun changeScene() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) - underTest.changeScene(SceneKey.Shade, "reason") - assertThat(currentScene).isEqualTo(SceneKey.Shade) + underTest.changeScene(Scenes.Shade, "reason") + assertThat(currentScene).isEqualTo(Scenes.Shade) } @Test fun changeScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() - underTest.changeScene(SceneKey.Gone, "reason") - assertThat(currentScene).isEqualTo(SceneKey.Gone) + underTest.changeScene(Scenes.Gone, "reason") + assertThat(currentScene).isEqualTo(Scenes.Gone) } @Test(expected = IllegalStateException::class) @@ -91,18 +91,18 @@ class SceneInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.fakeDeviceEntryRepository.setUnlocked(false) - underTest.changeScene(SceneKey.Gone, "reason") + underTest.changeScene(Scenes.Gone, "reason") } @Test fun sceneChanged_inDataSource() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) - fakeSceneDataSource.changeScene(SceneKey.Shade) + fakeSceneDataSource.changeScene(Scenes.Shade) - assertThat(currentScene).isEqualTo(SceneKey.Shade) + assertThat(currentScene).isEqualTo(Scenes.Shade) } @Test @@ -111,7 +111,7 @@ class SceneInteractorTest : SysuiTestCase() { val underTest = kosmos.sceneContainerRepository val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Lockscreen) + ObservableTransitionState.Idle(Scenes.Lockscreen) ) underTest.setTransitionState(transitionState) val reflectedTransitionState by collectLastValue(underTest.transitionState) @@ -120,8 +120,8 @@ class SceneInteractorTest : SysuiTestCase() { val progress = MutableStateFlow(1f) transitionState.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, - toScene = SceneKey.Shade, + fromScene = Scenes.Lockscreen, + toScene = Scenes.Shade, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -153,27 +153,27 @@ class SceneInteractorTest : SysuiTestCase() { val transitionTo by collectLastValue(underTest.transitioningTo) assertThat(transitionTo).isNull() - underTest.changeScene(SceneKey.Shade, "reason") + underTest.changeScene(Scenes.Shade, "reason") assertThat(transitionTo).isNull() val progress = MutableStateFlow(0f) transitionState.value = ObservableTransitionState.Transition( fromScene = underTest.currentScene.value, - toScene = SceneKey.Shade, + toScene = Scenes.Shade, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) - assertThat(transitionTo).isEqualTo(SceneKey.Shade) + assertThat(transitionTo).isEqualTo(Scenes.Shade) progress.value = 0.5f - assertThat(transitionTo).isEqualTo(SceneKey.Shade) + assertThat(transitionTo).isEqualTo(Scenes.Shade) progress.value = 1f - assertThat(transitionTo).isEqualTo(SceneKey.Shade) + assertThat(transitionTo).isEqualTo(Scenes.Shade) - transitionState.value = ObservableTransitionState.Idle(SceneKey.Shade) + transitionState.value = ObservableTransitionState.Idle(Scenes.Shade) assertThat(transitionTo).isNull() } @@ -182,7 +182,7 @@ class SceneInteractorTest : SysuiTestCase() { testScope.runTest { val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Shade) + ObservableTransitionState.Idle(Scenes.Shade) ) val isTransitionUserInputOngoing by collectLastValue(underTest.isTransitionUserInputOngoing) @@ -197,8 +197,8 @@ class SceneInteractorTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, + fromScene = Scenes.Shade, + toScene = Scenes.Lockscreen, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), @@ -217,8 +217,8 @@ class SceneInteractorTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, + fromScene = Scenes.Shade, + toScene = Scenes.Lockscreen, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), @@ -232,8 +232,8 @@ class SceneInteractorTest : SysuiTestCase() { transitionState.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, + fromScene = Scenes.Shade, + toScene = Scenes.Lockscreen, progress = flowOf(0.6f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), @@ -248,8 +248,8 @@ class SceneInteractorTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, + fromScene = Scenes.Shade, + toScene = Scenes.Lockscreen, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), @@ -261,7 +261,7 @@ class SceneInteractorTest : SysuiTestCase() { assertThat(isTransitionUserInputOngoing).isTrue() - transitionState.value = ObservableTransitionState.Idle(scene = SceneKey.Lockscreen) + transitionState.value = ObservableTransitionState.Idle(scene = Scenes.Lockscreen) assertThat(isTransitionUserInputOngoing).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 4e1623661a58..cc66f8b2f387 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -24,6 +24,8 @@ import android.platform.test.annotations.EnableFlags import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags as AconfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository @@ -46,8 +48,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository @@ -136,42 +137,42 @@ class SceneContainerStartableTest : SysuiTestCase() { val transitionStateFlow = prepareState( isDeviceUnlocked = true, - initialSceneKey = SceneKey.Gone, + initialSceneKey = Scenes.Gone, ) - assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentDesiredSceneKey).isEqualTo(Scenes.Gone) assertThat(isVisible).isTrue() underTest.start() assertThat(isVisible).isFalse() fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Shade, "reason") + sceneInteractor.changeScene(Scenes.Shade, "reason") transitionStateFlow.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Shade, + fromScene = Scenes.Gone, + toScene = Scenes.Shade, progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Shade) - transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade) + fakeSceneDataSource.unpause(expectedScene = Scenes.Shade) + transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Shade) assertThat(isVisible).isTrue() fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") transitionStateFlow.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Gone, + fromScene = Scenes.Shade, + toScene = Scenes.Gone, progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() - fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone) - transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) assertThat(isVisible).isFalse() kosmos.headsUpNotificationRepository.hasPinnedHeadsUp.value = true @@ -187,7 +188,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val isVisible by collectLastValue(sceneInteractor.isVisible) prepareState( isDeviceUnlocked = true, - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, isDeviceProvisioned = false, isFrpActive = true, ) @@ -214,7 +215,7 @@ class SceneContainerStartableTest : SysuiTestCase() { underTest.start() runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -223,14 +224,14 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( isDeviceUnlocked = true, - initialSceneKey = SceneKey.Gone, + initialSceneKey = Scenes.Gone, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) underTest.start() kosmos.fakeDeviceEntryRepository.setUnlocked(false) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -239,14 +240,14 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( isDeviceUnlocked = false, - initialSceneKey = SceneKey.Bouncer, + initialSceneKey = Scenes.Bouncer, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) underTest.start() kosmos.fakeDeviceEntryRepository.setUnlocked(true) - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -255,14 +256,14 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( isBypassEnabled = true, - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) underTest.start() kosmos.fakeDeviceEntryRepository.setUnlocked(true) - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -271,16 +272,16 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( isBypassEnabled = false, - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) underTest.start() // Authenticate using a passive auth method like face auth while bypass is disabled. faceAuthRepository.isAuthenticated.value = true kosmos.fakeDeviceEntryRepository.setUnlocked(true) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -291,19 +292,19 @@ class SceneContainerStartableTest : SysuiTestCase() { prepareState( isBypassEnabled = true, authenticationMethod = AuthenticationMethodModel.Pin, - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, ) underTest.start() runCurrent() - sceneInteractor.changeScene(SceneKey.Shade, "switch to shade") - transitionStateFlowValue.value = ObservableTransitionState.Idle(SceneKey.Shade) - assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + sceneInteractor.changeScene(Scenes.Shade, "switch to shade") + transitionStateFlowValue.value = ObservableTransitionState.Idle(Scenes.Shade) + assertThat(currentSceneKey).isEqualTo(Scenes.Shade) kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + assertThat(currentSceneKey).isEqualTo(Scenes.Shade) } @Test @@ -312,16 +313,16 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( isBypassEnabled = false, - initialSceneKey = SceneKey.Bouncer, + initialSceneKey = Scenes.Bouncer, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) underTest.start() // Authenticate using a passive auth method like face auth while bypass is disabled. faceAuthRepository.isAuthenticated.value = true kosmos.fakeDeviceEntryRepository.setUnlocked(true) - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -330,13 +331,13 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( isDeviceUnlocked = false, - initialSceneKey = SceneKey.Shade, + initialSceneKey = Scenes.Shade, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + assertThat(currentSceneKey).isEqualTo(Scenes.Shade) underTest.start() powerInteractor.setAsleepForTest() - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -348,14 +349,14 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(sysUiState) listOf( - SceneKey.Gone, - SceneKey.Lockscreen, - SceneKey.Bouncer, - SceneKey.Shade, - SceneKey.QuickSettings, + Scenes.Gone, + Scenes.Lockscreen, + Scenes.Bouncer, + Scenes.Shade, + Scenes.QuickSettings, ) .forEachIndexed { index, sceneKey -> - if (sceneKey == SceneKey.Gone) { + if (sceneKey == Scenes.Gone) { kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() } @@ -379,15 +380,15 @@ class SceneContainerStartableTest : SysuiTestCase() { testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.None, isLockscreenEnabled = false, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) underTest.start() powerInteractor.setAwakeForTest() - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -395,15 +396,15 @@ class SceneContainerStartableTest : SysuiTestCase() { testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.None, isLockscreenEnabled = true, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) underTest.start() powerInteractor.setAwakeForTest() - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -411,14 +412,14 @@ class SceneContainerStartableTest : SysuiTestCase() { testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) underTest.start() powerInteractor.setAwakeForTest() - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -426,12 +427,12 @@ class SceneContainerStartableTest : SysuiTestCase() { testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, startsAwake = false ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) underTest.start() kosmos.fakeDeviceEntryRepository.setUnlocked(true) @@ -439,14 +440,14 @@ class SceneContainerStartableTest : SysuiTestCase() { powerInteractor.setAwakeForTest() runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) } @Test fun collectFalsingSignals_onSuccessfulUnlock() = testScope.runTest { prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -456,11 +457,11 @@ class SceneContainerStartableTest : SysuiTestCase() { // Move around scenes without unlocking. listOf( - SceneKey.Shade, - SceneKey.QuickSettings, - SceneKey.Shade, - SceneKey.Lockscreen, - SceneKey.Bouncer, + Scenes.Shade, + Scenes.QuickSettings, + Scenes.Shade, + Scenes.Lockscreen, + Scenes.Bouncer, ) .forEach { sceneKey -> sceneInteractor.changeScene(sceneKey, "reason") @@ -471,17 +472,17 @@ class SceneContainerStartableTest : SysuiTestCase() { // Changing to the Gone scene should report a successful unlock. kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") runCurrent() verify(falsingCollector).onSuccessfulUnlock() // Move around scenes without changing back to Lockscreen, shouldn't report another // unlock. listOf( - SceneKey.Shade, - SceneKey.QuickSettings, - SceneKey.Shade, - SceneKey.Gone, + Scenes.Shade, + Scenes.QuickSettings, + Scenes.Shade, + Scenes.Gone, ) .forEach { sceneKey -> sceneInteractor.changeScene(sceneKey, "reason") @@ -490,17 +491,17 @@ class SceneContainerStartableTest : SysuiTestCase() { } // Changing to the Lockscreen scene shouldn't report a successful unlock. - sceneInteractor.changeScene(SceneKey.Lockscreen, "reason") + sceneInteractor.changeScene(Scenes.Lockscreen, "reason") runCurrent() verify(falsingCollector, times(1)).onSuccessfulUnlock() // Move around scenes without unlocking. listOf( - SceneKey.Shade, - SceneKey.QuickSettings, - SceneKey.Shade, - SceneKey.Lockscreen, - SceneKey.Bouncer, + Scenes.Shade, + Scenes.QuickSettings, + Scenes.Shade, + Scenes.Lockscreen, + Scenes.Bouncer, ) .forEach { sceneKey -> sceneInteractor.changeScene(sceneKey, "reason") @@ -509,7 +510,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } // Changing to the Gone scene should report a second successful unlock. - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") runCurrent() verify(falsingCollector, times(2)).onSuccessfulUnlock() } @@ -518,7 +519,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun collectFalsingSignals_setShowingAod() = testScope.runTest { prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -540,7 +541,7 @@ class SceneContainerStartableTest : SysuiTestCase() { testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Password, isDeviceUnlocked = false, ) @@ -550,7 +551,7 @@ class SceneContainerStartableTest : SysuiTestCase() { bouncerInteractor.onImeHiddenByUser() runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -559,7 +560,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeKeyguardRepository.setAodAvailable(false) runCurrent() prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, startsAwake = false, @@ -607,7 +608,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeKeyguardRepository.setAodAvailable(true) runCurrent() prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -652,7 +653,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun collectFalsingSignals_bouncerVisibility() = testScope.runTest { prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -660,13 +661,13 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() verify(falsingCollector).onBouncerHidden() - sceneInteractor.changeScene(SceneKey.Bouncer, "reason") + sceneInteractor.changeScene(Scenes.Bouncer, "reason") runCurrent() verify(falsingCollector).onBouncerShown() kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") runCurrent() verify(falsingCollector, times(2)).onBouncerHidden() } @@ -677,7 +678,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -687,7 +688,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) } @Test @@ -697,7 +698,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Bouncer, + initialSceneKey = Scenes.Bouncer, authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -706,7 +707,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -716,7 +717,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, authenticationMethod = AuthenticationMethodModel.None, isDeviceUnlocked = true, isLockscreenEnabled = false, @@ -726,7 +727,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false runCurrent() - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -736,9 +737,9 @@ class SceneContainerStartableTest : SysuiTestCase() { val transitionStateFlow = prepareState( isDeviceUnlocked = true, - initialSceneKey = SceneKey.Gone, + initialSceneKey = Scenes.Gone, ) - assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentDesiredSceneKey).isEqualTo(Scenes.Gone) verify(windowController, never()).setNotificationShadeFocusable(anyBoolean()) underTest.start() @@ -746,11 +747,11 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(windowController, times(1)).setNotificationShadeFocusable(false) fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Shade, "reason") + sceneInteractor.changeScene(Scenes.Shade, "reason") transitionStateFlow.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Shade, + fromScene = Scenes.Gone, + toScene = Scenes.Shade, progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -758,17 +759,17 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() verify(windowController, times(1)).setNotificationShadeFocusable(false) - fakeSceneDataSource.unpause(expectedScene = SceneKey.Shade) - transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade) + fakeSceneDataSource.unpause(expectedScene = Scenes.Shade) + transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Shade) runCurrent() verify(windowController, times(1)).setNotificationShadeFocusable(true) fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") transitionStateFlow.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Gone, + fromScene = Scenes.Shade, + toScene = Scenes.Gone, progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -776,8 +777,8 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() verify(windowController, times(1)).setNotificationShadeFocusable(true) - fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone) - transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) runCurrent() verify(windowController, times(2)).setNotificationShadeFocusable(false) } @@ -787,7 +788,7 @@ class SceneContainerStartableTest : SysuiTestCase() { testScope.runTest { val transitionStateFlow = prepareState( - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, ) underTest.start() runCurrent() @@ -796,7 +797,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Bouncer, + toScene = Scenes.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -815,7 +816,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -834,7 +835,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Shade, + toScene = Scenes.Shade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -853,7 +854,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -872,7 +873,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.QuickSettings, + toScene = Scenes.QuickSettings, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -891,7 +892,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val transitionStateFlow = prepareState( isDeviceUnlocked = true, - initialSceneKey = SceneKey.Gone, + initialSceneKey = Scenes.Gone, ) underTest.start() verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) @@ -899,7 +900,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Bouncer, + toScene = Scenes.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -914,7 +915,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -929,7 +930,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Shade, + toScene = Scenes.Shade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -944,7 +945,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -959,7 +960,7 @@ class SceneContainerStartableTest : SysuiTestCase() { clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, - toScene = SceneKey.QuickSettings, + toScene = Scenes.QuickSettings, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -978,12 +979,12 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.currentScene) val transitionStateFlow = prepareState() underTest.start() - emulateSceneTransition(transitionStateFlow, toScene = SceneKey.Bouncer) - assertThat(currentScene).isNotEqualTo(SceneKey.Lockscreen) + emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer) + assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen) kosmos.falsingManager.sendFalsingBelief() - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } private fun TestScope.emulateSceneTransition( @@ -1033,7 +1034,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } } - check(initialSceneKey != SceneKey.Gone || isDeviceUnlocked) { + check(initialSceneKey != Scenes.Gone || isDeviceUnlocked) { "Cannot start on the Gone scene and have the device be locked at the same time." } @@ -1043,7 +1044,7 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() val transitionStateFlow = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Lockscreen) + ObservableTransitionState.Idle(Scenes.Lockscreen) ) sceneInteractor.setTransitionState(transitionStateFlow) initialSceneKey?.let { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt index ed4b1e6a43c9..32c0172071f6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt @@ -53,9 +53,9 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) underTest.setDelegate(null) - assertThat(currentScene).isNotEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isNotEqualTo(Scenes.Bouncer) - underTest.changeScene(toScene = SceneKey.Bouncer) + underTest.changeScene(toScene = Scenes.Bouncer) assertThat(currentScene).isEqualTo(initialSceneKey) } @@ -71,11 +71,11 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { fun currentScene_withDelegate_changesScenes() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isNotEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isNotEqualTo(Scenes.Bouncer) - underTest.changeScene(toScene = SceneKey.Bouncer) + underTest.changeScene(toScene = Scenes.Bouncer) - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test @@ -83,8 +83,8 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = SceneKey.Bouncer) + fakeSceneDataSource.changeScene(toScene = Scenes.Bouncer) - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt index 27ae8b60009c..7b0127e94fb7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt @@ -30,7 +30,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock @@ -89,20 +89,20 @@ class SceneContainerViewModelTest : SysuiTestCase() { fun sceneTransition() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) - fakeSceneDataSource.changeScene(SceneKey.Shade) + fakeSceneDataSource.changeScene(Scenes.Shade) - assertThat(currentScene).isEqualTo(SceneKey.Shade) + assertThat(currentScene).isEqualTo(Scenes.Shade) } @Test fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = SceneKey.Gone) + fakeSceneDataSource.changeScene(toScene = Scenes.Gone) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) sceneContainerConfig.sceneKeys .filter { it != currentScene } @@ -117,9 +117,9 @@ class SceneContainerViewModelTest : SysuiTestCase() { fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen) + fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) sceneContainerConfig.sceneKeys .filter { it != currentScene } @@ -135,15 +135,15 @@ class SceneContainerViewModelTest : SysuiTestCase() { testScope.runTest { falsingManager.setIsFalseTouch(true) val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen) + fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) sceneContainerConfig.sceneKeys .filter { it != currentScene } .filter { // Moving to the Communal scene is not currently falsing protected. - it != SceneKey.Communal + it != Scenes.Communal } .forEach { toScene -> assertWithMessage("Protected scene $toScene not properly protected") @@ -157,14 +157,14 @@ class SceneContainerViewModelTest : SysuiTestCase() { testScope.runTest { falsingManager.setIsFalseTouch(true) val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen) + fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) sceneContainerConfig.sceneKeys .filter { // Moving to the Communal scene is not currently falsing protected. - it == SceneKey.Communal + it == Scenes.Communal } .forEach { toScene -> assertWithMessage("Unprotected scene $toScene is incorrectly protected") @@ -178,9 +178,9 @@ class SceneContainerViewModelTest : SysuiTestCase() { testScope.runTest { falsingManager.setIsFalseTouch(true) val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = SceneKey.Gone) + fakeSceneDataSource.changeScene(toScene = Scenes.Gone) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) sceneContainerConfig.sceneKeys .filter { it != currentScene } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt index ec424b05fc06..d3fa3603d722 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt @@ -18,6 +18,8 @@ package com.android.systemui.shade import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -28,8 +30,7 @@ import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.CommandQueue @@ -87,7 +88,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() { runCurrent() // THEN the shade remains collapsed and the post-collapse action ran - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Gone) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) verify(testRunnable, times(1)).run() } @@ -105,7 +106,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() { runCurrent() // THEN the shade remains expanded and the post-collapse action did not run - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Shade) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade) assertThat(shadeInteractor.isAnyFullyExpanded.value).isTrue() verify(testRunnable, never()).run() } @@ -122,7 +123,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() { runCurrent() // THEN the shade collapses back to lockscreen and the post-collapse action ran - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Lockscreen) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen) } @Test @@ -137,7 +138,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() { runCurrent() // THEN the shade collapses back to lockscreen and the post-collapse action ran - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Gone) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) } @Test @@ -181,21 +182,21 @@ class ShadeControllerSceneImplTest : SysuiTestCase() { private fun setDeviceEntered(isEntered: Boolean) { setScene( if (isEntered) { - SceneKey.Gone + Scenes.Gone } else { - SceneKey.Lockscreen + Scenes.Lockscreen } ) assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) } private fun setCollapsed() { - setScene(SceneKey.Gone) + setScene(Scenes.Gone) assertThat(shadeInteractor.isAnyExpanded.value).isFalse() } private fun setShadeFullyExpanded() { - setScene(SceneKey.Shade) + setScene(Scenes.Shade) assertThat(shadeInteractor.isAnyFullyExpanded.value).isTrue() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt index 1ef07facf8d1..bb40591335f4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt @@ -18,12 +18,12 @@ package com.android.systemui.shade.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -53,8 +53,8 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Shade, + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, progress = MutableStateFlow(.1f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -76,8 +76,8 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Gone, + fromScene = Scenes.QuickSettings, + toScene = Scenes.Gone, progress = MutableStateFlow(.1f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -99,8 +99,8 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Gone, + fromScene = Scenes.QuickSettings, + toScene = Scenes.Gone, progress = MutableStateFlow(.1f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(true), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt index ec4da0405b6d..b66213330496 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt @@ -19,13 +19,14 @@ package com.android.systemui.shade.domain.interactor import android.content.applicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.recents.utilities.Utilities import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -56,45 +57,45 @@ class ShadeBackActionInteractorImplTest : SysuiTestCase() { @Test fun animateCollapseQs_notOnQs() = testScope.runTest { - setScene(SceneKey.Shade) + setScene(Scenes.Shade) underTest.animateCollapseQs(true) runCurrent() - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Shade) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade) } @Test fun animateCollapseQs_fullyCollapse_entered() = testScope.runTest { enterDevice() - setScene(SceneKey.QuickSettings) + setScene(Scenes.QuickSettings) underTest.animateCollapseQs(true) runCurrent() - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Gone) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) } @Test fun animateCollapseQs_fullyCollapse_locked() = testScope.runTest { deviceEntryRepository.setUnlocked(false) - setScene(SceneKey.QuickSettings) + setScene(Scenes.QuickSettings) underTest.animateCollapseQs(true) runCurrent() - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Lockscreen) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen) } @Test fun animateCollapseQs_notFullyCollapse() = testScope.runTest { - setScene(SceneKey.QuickSettings) + setScene(Scenes.QuickSettings) underTest.animateCollapseQs(false) runCurrent() - assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Shade) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade) } private fun enterDevice() { deviceEntryRepository.setUnlocked(true) testScope.runCurrent() - setScene(SceneKey.Gone) + setScene(Scenes.Gone) } private fun setScene(key: SceneKey) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt index bf136cdd817e..4cd2c301d6ee 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.shade.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue @@ -27,8 +28,7 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.userRepository import com.google.common.truth.Truth @@ -67,8 +67,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Shade, + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, progress = MutableStateFlow(.3f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -96,8 +96,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Shade, + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -120,8 +120,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Shade, + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, progress = MutableStateFlow(.3f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -143,7 +143,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { keyguardRepository.setStatusBarState(StatusBarState.SHADE) val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Shade) + ObservableTransitionState.Idle(Scenes.Shade) ) sceneInteractor.setTransitionState(transitionState) runCurrent() @@ -161,7 +161,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { keyguardRepository.setStatusBarState(StatusBarState.SHADE) val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.QuickSettings) + ObservableTransitionState.Idle(Scenes.QuickSettings) ) sceneInteractor.setTransitionState(transitionState) runCurrent() @@ -174,7 +174,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun lockscreenShadeExpansion_idle_onScene() = testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene - val key = SceneKey.Shade + val key = Scenes.Shade val expansion = underTest.sceneBasedExpansion(sceneInteractor, key) val expansionAmount by collectLastValue(expansion) @@ -191,13 +191,13 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun lockscreenShadeExpansion_idle_onDifferentScene() = testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene - val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade) + val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.Shade) val expansionAmount by collectLastValue(expansion) // WHEN transition state is idle on a different scene val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Lockscreen) + ObservableTransitionState.Idle(Scenes.Lockscreen) ) sceneInteractor.setTransitionState(transitionState) @@ -209,7 +209,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun lockscreenShadeExpansion_transitioning_toScene() = testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene - val key = SceneKey.QuickSettings + val key = Scenes.QuickSettings val expansion = underTest.sceneBasedExpansion(sceneInteractor, key) val expansionAmount by collectLastValue(expansion) @@ -218,7 +218,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, + fromScene = Scenes.Lockscreen, toScene = key, progress = progress, isInitiatedByUserInput = false, @@ -247,7 +247,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun lockscreenShadeExpansion_transitioning_fromScene() = testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene - val key = SceneKey.QuickSettings + val key = Scenes.QuickSettings val expansion = underTest.sceneBasedExpansion(sceneInteractor, key) val expansionAmount by collectLastValue(expansion) @@ -257,7 +257,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -290,8 +290,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.QuickSettings, + fromScene = Scenes.Gone, + toScene = Scenes.QuickSettings, progress = MutableStateFlow(.1f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -313,8 +313,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.QuickSettings, + fromScene = Scenes.Shade, + toScene = Scenes.QuickSettings, progress = MutableStateFlow(.1f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -331,7 +331,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() = testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene - val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings) + val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings) val expansionAmount by collectLastValue(expansion) // WHEN transition state is starting to between different scenes @@ -339,8 +339,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, - toScene = SceneKey.Shade, + fromScene = Scenes.Lockscreen, + toScene = Scenes.Shade, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -368,7 +368,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun userInteracting_idle() = testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene - val key = SceneKey.Shade + val key = Scenes.Shade val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) @@ -385,7 +385,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun userInteracting_transitioning_toScene_programmatic() = testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene - val key = SceneKey.QuickSettings + val key = Scenes.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) @@ -394,7 +394,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, + fromScene = Scenes.Lockscreen, toScene = key, progress = progress, isInitiatedByUserInput = false, @@ -423,7 +423,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun userInteracting_transitioning_toScene_userInputDriven() = testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene - val key = SceneKey.QuickSettings + val key = Scenes.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) @@ -432,7 +432,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, + fromScene = Scenes.Lockscreen, toScene = key, progress = progress, isInitiatedByUserInput = true, @@ -461,7 +461,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun userInteracting_transitioning_fromScene_programmatic() = testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene - val key = SceneKey.QuickSettings + val key = Scenes.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) @@ -471,7 +471,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, progress = progress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -499,7 +499,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun userInteracting_transitioning_fromScene_userInputDriven() = testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene - val key = SceneKey.QuickSettings + val key = Scenes.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) @@ -509,7 +509,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, progress = progress, isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), @@ -537,7 +537,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { fun userInteracting_transitioning_toAndFromDifferentScenes() = testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene - val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade) + val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, Scenes.Shade) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to between different scenes @@ -545,8 +545,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( - fromScene = SceneKey.Lockscreen, - toScene = SceneKey.QuickSettings, + fromScene = Scenes.Lockscreen, + toScene = Scenes.QuickSettings, progress = MutableStateFlow(0f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index d655ade5cf2c..853b00d345bc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -30,7 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.privacyChipInteractor import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel @@ -125,7 +125,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { ) kosmos.fakeDeviceEntryRepository.setUnlocked(false) - assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(upTransitionSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -137,7 +137,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { ) kosmos.fakeDeviceEntryRepository.setUnlocked(true) - assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) + assertThat(upTransitionSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -148,9 +148,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) - sceneInteractor.changeScene(SceneKey.Lockscreen, "reason") + sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(upTransitionSceneKey).isEqualTo(Scenes.Lockscreen) } @Test @@ -163,9 +163,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() { AuthenticationMethodModel.None ) runCurrent() - sceneInteractor.changeScene(SceneKey.Gone, "reason") + sceneInteractor.changeScene(Scenes.Gone, "reason") - assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) + assertThat(upTransitionSceneKey).isEqualTo(Scenes.Gone) } @Test @@ -206,7 +206,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { underTest.onContentClicked() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) } @Test @@ -221,7 +221,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { underTest.onContentClicked() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt index efd8f000df41..47918c8c1b3d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.coroutines.collectLastValue @@ -28,8 +29,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationStackAppearanceViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel @@ -92,19 +92,19 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { testScope.runTest { val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(scene = SceneKey.Gone) + ObservableTransitionState.Idle(scene = Scenes.Gone) ) sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(appearanceViewModel.expandFraction) assertThat(expandFraction).isEqualTo(0f) fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.Shade, "reason") + sceneInteractor.changeScene(Scenes.Shade, "reason") val transitionProgress = MutableStateFlow(0f) transitionState.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Shade, + fromScene = Scenes.Gone, + toScene = Scenes.Shade, progress = transitionProgress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -117,7 +117,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { assertThat(expandFraction).isWithin(0.01f).of(progress) } - fakeSceneDataSource.unpause(expectedScene = SceneKey.Shade) + fakeSceneDataSource.unpause(expectedScene = Scenes.Shade) assertThat(expandFraction).isWithin(0.01f).of(1f) } @@ -126,7 +126,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { testScope.runTest { val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(scene = SceneKey.Lockscreen) + ObservableTransitionState.Idle(scene = Scenes.Lockscreen) ) sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(appearanceViewModel.expandFraction) @@ -138,19 +138,19 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { testScope.runTest { val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(scene = SceneKey.Shade) + ObservableTransitionState.Idle(scene = Scenes.Shade) ) sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(appearanceViewModel.expandFraction) assertThat(expandFraction).isEqualTo(1f) fakeSceneDataSource.pause() - sceneInteractor.changeScene(SceneKey.QuickSettings, "reason") + sceneInteractor.changeScene(Scenes.QuickSettings, "reason") val transitionProgress = MutableStateFlow(0f) transitionState.value = ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.QuickSettings, + fromScene = Scenes.Shade, + toScene = Scenes.QuickSettings, progress = transitionProgress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), @@ -163,7 +163,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { assertThat(expandFraction).isEqualTo(1f) } - fakeSceneDataSource.unpause(expectedScene = SceneKey.QuickSettings) + fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings) assertThat(expandFraction).isEqualTo(1f) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index 7f5a658587f3..0de15b8db665 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -21,13 +21,13 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.communal.domain.interactor.communalInteractor -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -278,8 +278,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) ) val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -391,8 +391,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { // Move to glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt index 243aab24b07d..dcf635e622f4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt @@ -32,8 +32,8 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.volume.localMediaRepository import com.android.systemui.volume.mediaController import com.android.systemui.volume.mediaControllerRepository +import com.android.systemui.volume.mediaOutputActionsInteractor import com.android.systemui.volume.mediaOutputInteractor -import com.android.systemui.volume.panel.mediaOutputActionsInteractor import com.android.systemui.volume.panel.volumePanelViewModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml index 68c8dd96d188..d8d298573d04 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf.xml @@ -19,11 +19,11 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/half_shelf_dialog" android:orientation="vertical" - android:layout_width="wrap_content" + android:layout_width="@dimen/large_dialog_width" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|bottom" - android:paddingStart="4dp" - android:paddingEnd="4dp"> + android:paddingLeft="@dimen/dialog_side_padding" + android:paddingRight="@dimen/dialog_side_padding"> <LinearLayout android:id="@+id/half_shelf" diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml index bb8de4c32e76..0dcd15b429c1 100644 --- a/packages/SystemUI/res/layout/scene_window_root.xml +++ b/packages/SystemUI/res/layout/scene_window_root.xml @@ -24,7 +24,7 @@ android:id="@+id/scene_window_root" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="false"> + android:fitsSystemWindows="true"> <include layout="@layout/super_notification_shade" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index c4d7f8bbb349..515ef619141b 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Lui"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibreer"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Demp"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Saai uit"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Onbeskikbaar omdat luitoon gedemp is"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om te ontdemp."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string> diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml index 662aa717ffc1..1c9a79414ff1 100644 --- a/packages/SystemUI/res/values-af/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Af"</item> <item msgid="578444932039713369">"Aan"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Onbeskikbaar"</item> <item msgid="8707481475312432575">"Af"</item> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 37ba3c182422..9763ff2ae468 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"ጥሪ"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ንዘር"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"ድምጸ-ከል አድርግ"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"የጥሪ ድምጽ ስለተዘጋ አይገኝም"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml index e5d68d985792..3fb24b983162 100644 --- a/packages/SystemUI/res/values-am/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ጠፍቷል"</item> <item msgid="578444932039713369">"በርቷል"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"አይገኝም"</item> <item msgid="8707481475312432575">"ጠፍቷል"</item> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 1de96aea2210..1b7e3037103f 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"استصدار رنين"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"اهتزاز"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"كتم الصوت"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"البثّ"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"يتعذّر التغيير بسبب كتم صوت الرنين."</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. انقر لإلغاء التجاهل."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml index 856ae1db8fa1..cf050ac26473 100644 --- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"الميزة غير مفعّلة"</item> <item msgid="578444932039713369">"الميزة مفعّلة"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"الميزة غير متاحة"</item> <item msgid="8707481475312432575">"الميزة غير مفعّلة"</item> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index da69f77836a1..429f03ed3a63 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"ৰিং"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"কম্পন"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"মিউট"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"কাষ্ট কৰক"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"ৰিং মিউট কৰি থোৱাৰ বাবে উপলব্ধ নহয়"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। আনমিউট কৰিবৰ বাবে টিপক।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পনৰ বাবে টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট কৰিবলৈ টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml index a9c3e3b997b7..f4268ed17ff5 100644 --- a/packages/SystemUI/res/values-as/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"অফ আছে"</item> <item msgid="578444932039713369">"অন কৰা আছে"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"উপলব্ধ নহয়"</item> <item msgid="8707481475312432575">"অফ আছে"</item> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 8857ddb72b47..639cbbc4cef1 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zəng"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrasiya"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Susdurun"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Yayım"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zəng səssiz edildiyi üçün əlçatan deyil"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Səsli etmək üçün tıklayın."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml index d973e4ead3c2..eeb81ccf42e1 100644 --- a/packages/SystemUI/res/values-az/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Deaktiv"</item> <item msgid="578444932039713369">"Aktiv"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Əlçatan deyil"</item> <item msgid="8707481475312432575">"Deaktiv"</item> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 677f169f8bc4..e97dbec9a690 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Aktiviraj zvono"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriraj"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Prebacivanje"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvuk isključen"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml index 32051ef19743..217d99975d3f 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Isključeno"</item> <item msgid="578444932039713369">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nedostupno"</item> <item msgid="8707481475312432575">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 7feb1607be22..3b06d05e76f5 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Званок"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вібрацыя"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Гук выключаны"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Трансляцыя"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недаступна, бо выключаны гук выклікаў"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дакраніцеся, каб уключыць гук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml index e71c29bd15de..717e4c9a9c39 100644 --- a/packages/SystemUI/res/values-be/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Выключана"</item> <item msgid="578444932039713369">"Уключана"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Недаступна"</item> <item msgid="8707481475312432575">"Выключана"</item> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index d684d650af94..643ef9cecbf1 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Позвъняване"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибриране"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Без звук"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Предаване"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Не е налице, защото звъненето е спряно"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Докоснете, за да включите отново звука."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml index 24b41d23e900..58fa82bbc77a 100644 --- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Изкл."</item> <item msgid="578444932039713369">"Вкл."</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Не е налице"</item> <item msgid="8707481475312432575">"Изкл."</item> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index db52b2ea1621..9a2f040c7e7e 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"রিং"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ভাইব্রেট"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"মিউট"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"কাস্ট করুন"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"রিং মিউট করা হয়েছে বলে উপলভ্য নেই"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। সশব্দ করতে আলতো চাপুন।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml index 59061c223580..5c3c66c29461 100644 --- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"বন্ধ আছে"</item> <item msgid="578444932039713369">"চালু আছে"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"উপলভ্য নেই"</item> <item msgid="8707481475312432575">"বন্ধ আছে"</item> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 3d54f6cfb747..db102a0ed68b 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvono"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Emitiraj"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno zbog isključenog zvona"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml index 32051ef19743..217d99975d3f 100644 --- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Isključeno"</item> <item msgid="578444932039713369">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nedostupno"</item> <item msgid="8707481475312432575">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index bcc451b60e16..c5287344c7c0 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Fes sonar"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibra"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silencia"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Emet"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible perquè el so està silenciat"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca per activar el so."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml index e99926c57324..c1ac5a356f05 100644 --- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desactivat"</item> <item msgid="578444932039713369">"Activat"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"No disponible"</item> <item msgid="8707481475312432575">"Desactivat"</item> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index bf7566a803ee..f29dabb388d8 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Vyzvánění"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrace"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ztlumení"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Odesílání"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, protože vyzvánění je ztlumené"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnete zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string> diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml index 6359f94112bb..0a4d4d0cfeef 100644 --- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Vypnuto"</item> <item msgid="578444932039713369">"Zapnuto"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nedostupné"</item> <item msgid="8707481475312432575">"Vypnuto"</item> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 60cf76a2d5e7..ec899f2f2c20 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Slå lyden fra"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ikke muligt, da ringetonen er slået fra"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryk for at slå lyden til."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string> diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml index 1daed4cd6864..2391753f8c0b 100644 --- a/packages/SystemUI/res/values-da/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Fra"</item> <item msgid="578444932039713369">"Til"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Ikke tilgængelig"</item> <item msgid="8707481475312432575">"Fra"</item> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 60165d24a260..f7e74c906960 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Klingeln lassen"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrieren"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Stummschalten"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Stream"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nicht verfügbar, da Klingelton stumm"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Zum Aufheben der Stummschaltung tippen."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml index 9a087472bd03..3aae04bbc931 100644 --- a/packages/SystemUI/res/values-de/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Aus"</item> <item msgid="578444932039713369">"An"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nicht verfügbar"</item> <item msgid="8707481475312432575">"Aus"</item> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 6c45adfbda4b..6b341fdf1d93 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Κουδούνισμα"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Δόνηση"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Σίγαση"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Μετάδοση"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Μη διαθέσιμο λόγω σίγασης ήχου κλήσης"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Πατήστε για κατάργηση σίγασης."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml index 4d94515ac130..035f1171d3b4 100644 --- a/packages/SystemUI/res/values-el/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Ανενεργό"</item> <item msgid="578444932039713369">"Ενεργό"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Μη διαθέσιμο"</item> <item msgid="8707481475312432575">"Ανενεργό"</item> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 3dbe2dc013f5..021f7db6c2b6 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string> <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string> diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml index 0cf28684aa48..2576b6080638 100644 --- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Off"</item> <item msgid="578444932039713369">"On"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Unavailable"</item> <item msgid="8707481475312432575">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml index 0cf28684aa48..2576b6080638 100644 --- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Off"</item> <item msgid="578444932039713369">"On"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Unavailable"</item> <item msgid="8707481475312432575">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 3dbe2dc013f5..021f7db6c2b6 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string> <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string> diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml index 0cf28684aa48..2576b6080638 100644 --- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Off"</item> <item msgid="578444932039713369">"On"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Unavailable"</item> <item msgid="8707481475312432575">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 3dbe2dc013f5..021f7db6c2b6 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string> <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string> diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml index 0cf28684aa48..2576b6080638 100644 --- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Off"</item> <item msgid="578444932039713369">"On"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Unavailable"</item> <item msgid="8707481475312432575">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml index b9c8e5fcdd6a..42daf8a6b23d 100644 --- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Off"</item> <item msgid="578444932039713369">"On"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Unavailable"</item> <item msgid="8707481475312432575">"Off"</item> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 15de0d5bacaf..1798e9aa4b2d 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Agregar más widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén presionado para personalizar los widgets"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícono de la app de widget inhabilitado"</string> <string name="edit_widget" msgid="9030848101135393954">"Modificar widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Agregar widget"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Timbre"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Transmisión"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible por timbre silenciado"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Presiona para dejar de silenciar."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de encendido"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"Puedes ubicar este teléfono con Encontrar mi dispositivo, incluso si está apagado"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"Apagando…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantenimiento"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desenchufa el dispositivo"</string> diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml index bb3983b0bb09..09abc543dbcf 100644 --- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desactivado"</item> <item msgid="578444932039713369">"Activado"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"No disponible"</item> <item msgid="8707481475312432575">"Desactivado"</item> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 3edc603034f1..73c913fe9a6d 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Hacer sonar"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Enviar"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible (el tono está silenciado)"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar el sonido."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string> diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml index 66c7ee5efe6c..83b4627897da 100644 --- a/packages/SystemUI/res/values-es/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desactivado"</item> <item msgid="578444932039713369">"Activado"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"No disponible"</item> <item msgid="8707481475312432575">"Desactivado"</item> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 7ae5219795a9..6fa704490367 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Helisemine"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibreerimine"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Vaigistatud"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Ülekandmine"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Pole saadaval, kuna helin on vaigistatud"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Puudutage vaigistuse tühistamiseks."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml index 6a9edbbe812a..4f0551d7af74 100644 --- a/packages/SystemUI/res/values-et/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Väljas"</item> <item msgid="578444932039713369">"Sees"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Pole saadaval"</item> <item msgid="8707481475312432575">"Väljas"</item> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index d0b10a0db21d..564fbb395aeb 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Jo tonua"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dardara"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ez jo tonua"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Igorri"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ez dago erabilgarri, tonua desaktibatuta dagoelako"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sakatu audioa aktibatzeko."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> @@ -732,7 +730,7 @@ <string name="group_system_full_screenshot" msgid="5742204844232667785">"Atera pantaila-argazki bat"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Erakutsi lasterbideak"</string> <string name="group_system_go_back" msgid="2730322046244918816">"Egin atzera"</string> - <string name="group_system_access_home_screen" msgid="4130366993484706483">"Joan hasierako pantailara"</string> + <string name="group_system_access_home_screen" msgid="4130366993484706483">"Joan orri nagusira"</string> <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Ikusi azkenaldiko aplikazioak"</string> <string name="group_system_cycle_forward" msgid="5478663965957647805">"Ikusi azken aplikazioak banan-banan (aurrerantz)"</string> <string name="group_system_cycle_back" msgid="8194102916946802902">"Ikusi azken aplikazioak banan-banan (atzerantz)"</string> @@ -1096,7 +1094,7 @@ <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string> <string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Elkarrizketa-widgetak"</string> - <string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat hasierako pantailan gehitzeko"</string> + <string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat orri nagusian gehitzeko"</string> <string name="no_conversations_text" msgid="5354115541282395015">"Azken elkarrizketak agertuko dira hemen"</string> <string name="priority_conversations" msgid="3967482288896653039">"Lehentasunezko elkarrizketak"</string> <string name="recent_conversations" msgid="8531874684782574622">"Azken elkarrizketak"</string> diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml index d023076ca2bf..accecacbe8dd 100644 --- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desaktibatuta"</item> <item msgid="578444932039713369">"Aktibatuta"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Ez dago erabilgarri"</item> <item msgid="8707481475312432575">"Desaktibatuta"</item> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 0f937814e55d..95f17b0d1e56 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"زنگ زدن"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"لرزش"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"صامت"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"ارسال محتوا"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"دردسترس نیست، چون زنگ بیصدا شده است"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. برای باصدا کردن ضربه بزنید."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. برای تنظیم روی لرزش ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. برای صامت کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string> diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml index b341e9e64c7b..01a549ed70b3 100644 --- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"خاموش"</item> <item msgid="578444932039713369">"روشن"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"دردسترس نیست"</item> <item msgid="8707481475312432575">"خاموش"</item> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 5d3959d92b68..ab022dd80277 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Soittoääni"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Värinä"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Äänetön"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Striimaa"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ei käytettävissä, soittoääni mykistetty"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Poista mykistys koskettamalla."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml index bbd64fdb87da..f7a8ec94f1bd 100644 --- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Poissa päältä"</item> <item msgid="578444932039713369">"Päällä"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Ei saatavilla"</item> <item msgid="8707481475312432575">"Poissa päältä"</item> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index e02c75ceae3b..1186c81c1a10 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonnerie"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Sonnerie désactivée"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Diffuser"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible : sonnerie en sourdine"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Touchez pour réactiver le son."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml index b96984173d37..7b9708ef03f4 100644 --- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Désactivé"</item> <item msgid="578444932039713369">"Activé"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Non disponible"</item> <item msgid="8707481475312432575">"Désactivé"</item> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 2621a904fa33..a02c9f7f9e6f 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonnerie"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibreur"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Couper le son"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Caster"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponible, car la sonnerie est coupée"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Appuyez pour ne plus ignorer."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 34440a0d299b..af1d09d3db3d 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Désactivé"</item> <item msgid="578444932039713369">"Activé"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Indisponible"</item> <item msgid="8707481475312432575">"Désactivée"</item> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 1955c040990f..e758af880582 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Facer soar"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Emitir"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non dispoñible (o son está silenciado)"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar o son."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string> diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml index b03f31171e5c..a963decd28e9 100644 --- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Non"</item> <item msgid="578444932039713369">"Si"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Non dispoñible"</item> <item msgid="8707481475312432575">"Non"</item> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 5a1cf421da54..6d1d8df1bc27 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"રિંગ કરો"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"વાઇબ્રેટ"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"મ્યૂટ કરો"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"કાસ્ટ કરો"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"રિંગ મ્યૂટ કરી હોવાના કારણે અનુપલબ્ધ છે"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml index 5d1ad6ff0514..580ec104dfb4 100644 --- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"બંધ છે"</item> <item msgid="578444932039713369">"ચાલુ છે"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ઉપલબ્ધ નથી"</item> <item msgid="8707481475312432575">"બંધ છે"</item> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 4b8c959064af..2035429a00b9 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"आवाज़ चालू है"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"वाइब्रेशन"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"आवाज़ बंद है"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"कास्ट करें"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट होने से आवाज़ नहीं सुनाई दी"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करने के लिए टैप करें."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml index cd29fb92063f..3fd0b30ea4a0 100644 --- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"बंद है"</item> <item msgid="578444932039713369">"चालू है"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"उपलब्ध नहीं है"</item> <item msgid="8707481475312432575">"बंद है"</item> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 45666c07aa21..d50a95121148 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvonjenje"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Zvuk je isključen"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Emitiraj"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvono utišano"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string> diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml index 32051ef19743..217d99975d3f 100644 --- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Isključeno"</item> <item msgid="578444932039713369">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nedostupno"</item> <item msgid="8707481475312432575">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 3606d7486737..b09419bd396d 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Csörgés"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Rezgés"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Néma"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Átküldés"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem lehetséges, a csörgés le van némítva"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Koppintson a némítás megszüntetéséhez."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml index 157c552b31a6..fad2cd4e6d4b 100644 --- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Ki"</item> <item msgid="578444932039713369">"Be"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nem áll rendelkezésre"</item> <item msgid="8707481475312432575">"Ki"</item> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 1510291e00f2..4ea86d0571bb 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Սովորական"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Թրթռոց"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Անձայն"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Հեռարձակում"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Հասանելի չէ, երբ զանգի ձայնն անջատված է"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռումը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml index 089716fa51d6..380d9d2d9a95 100644 --- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Անջատված է"</item> <item msgid="578444932039713369">"Միացված է"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Հասանելի չէ"</item> <item msgid="8707481475312432575">"Անջատված է"</item> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 09a9938b5abb..188f3fbcd14d 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Dering"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Getar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Nonaktifkan"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Transmisi"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia karena volume dering dibisukan"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketuk untuk menyuarakan."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string> diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml index 71460a71b414..9be5d0276a24 100644 --- a/packages/SystemUI/res/values-in/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Nonaktif"</item> <item msgid="578444932039713369">"Aktif"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Tidak tersedia"</item> <item msgid="8707481475312432575">"Nonaktif"</item> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index bbc8eab4f9da..47756c213877 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Hringing"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Titringur"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Hljóð af"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Senda út"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ekki í boði þar sem hringing er þögguð"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ýttu til að hætta að þagga."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml index 17aaf6c2a877..1ee6e471fad3 100644 --- a/packages/SystemUI/res/values-is/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Slökkt"</item> <item msgid="578444932039713369">"Kveikt"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Ekki í boði"</item> <item msgid="8707481475312432575">"Slökkt"</item> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 7c09c597f2a5..5bad44c836a7 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Attiva suoneria"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Attiva vibrazione"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenzia"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Trasmissione"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non disponibile con l\'audio disattivato"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tocca per riattivare l\'audio."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string> diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml index 7aa09d420f87..28e28aed82b8 100644 --- a/packages/SystemUI/res/values-it/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Off"</item> <item msgid="578444932039713369">"On"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Non disponibile"</item> <item msgid="8707481475312432575">"Off"</item> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 644b599268ec..154de1523bbb 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"צלצול"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"רטט"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"השתקה"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"הפעלת Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"לא זמין כי הצלצול מושתק"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. יש להקיש כדי לבטל את ההשתקה."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. צריך להקיש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. יש להקיש כדי להשתיק. ייתכן ששירותי הנגישות יושתקו."</string> diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml index bd2a6f7d0d2b..bb3eb10fd719 100644 --- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"כבוי"</item> <item msgid="578444932039713369">"פועל"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"לא זמין"</item> <item msgid="8707481475312432575">"כבוי"</item> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 3e0d24586686..227edd3570d0 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ウィジェットの追加"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長押ししてウィジェットをカスタマイズ"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ウィジェットのカスタマイズ"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"無効なウィジェットのアプリアイコン"</string> <string name="edit_widget" msgid="9030848101135393954">"ウィジェットを編集"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"削除"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ウィジェットを追加"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"着信音"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"バイブレーション"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"ミュート"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"キャスト"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"着信音がミュートされているため利用できません"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。タップしてミュートを解除します。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源ボタン メニュー"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ページ <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"ロック画面"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"「デバイスを探す」を使うと、電源が OFF の状態でもこのスマートフォンの現在地を確認できます"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"シャットダウン中…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"取り扱いに関する手順をご覧ください"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"取り扱いに関する手順をご覧ください"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"デバイスを電源から外します"</string> diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml index 31158ca59882..ebadf3b385ce 100644 --- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"OFF"</item> <item msgid="578444932039713369">"ON"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"使用不可"</item> <item msgid="8707481475312432575">"OFF"</item> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 973af6fa5361..70eeb334556d 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"დარეკვა"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ვიბრაცია"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"დადუმება"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"ტრანსლირება"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"ზარის დადუმების გამო ხელმისაწვდომი არაა"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml index 366030a2231f..07a8a76b8c97 100644 --- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"გამორთულია"</item> <item msgid="578444932039713369">"ჩართულია"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"მიუწვდომელია"</item> <item msgid="8707481475312432575">"გამორთულია"</item> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index d5ae0ac6419f..c2525d9aec8f 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Шылдырлау"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Діріл"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Дыбысын өшіру"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Трансляциялау"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Қолжетімді емес, шылдырлату өшірулі."</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дыбысын қосу үшін түртіңіз."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml index b8089e44a5ce..f5b094855618 100644 --- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Өшірулі"</item> <item msgid="578444932039713369">"Қосулы"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Қолжетімсіз"</item> <item msgid="8707481475312432575">"Өшірулі"</item> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 11a284d2d9f4..0d2503359790 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"រោទ៍"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ញ័រ"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"បិទសំឡេង"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"បញ្ជូន"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"មិនអាចប្រើបានទេ ព្រោះសំឡេងរោទ៍ត្រូវបានបិទ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml index 8c5c8d1ce088..a2031b070a0c 100644 --- a/packages/SystemUI/res/values-km/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"បិទ"</item> <item msgid="578444932039713369">"បើក"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"មិនមានទេ"</item> <item msgid="8707481475312432575">"បិទ"</item> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index a311a8621af3..66b8e72ee2cf 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"ರಿಂಗ್"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ವೈಬ್ರೇಟ್"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"ಮ್ಯೂಟ್"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"ಬಿತ್ತರಿಸಿ"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"ರಿಂಗ್ ಮ್ಯೂಟ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ಅನ್ಮ್ಯೂಟ್ ಮಾಡುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml index 250eb5adc8de..de0fcae601cd 100644 --- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ಆಫ್"</item> <item msgid="578444932039713369">"ಆನ್"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ಲಭ್ಯವಿಲ್ಲ"</item> <item msgid="8707481475312432575">"ಆಫ್"</item> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 1fe5f90fd78f..028c8cf06eb9 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"벨소리"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"진동"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"음소거"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"전송"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"벨소리가 음소거되어 있으므로 사용할 수 없음"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. 탭하여 음소거를 해제하세요."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml index 7981d285946c..c9b2846ad9e1 100644 --- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"꺼짐"</item> <item msgid="578444932039713369">"켜짐"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"이용 불가"</item> <item msgid="8707481475312432575">"꺼짐"</item> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 789f7d806548..0f0055561759 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Шыңгыратуу"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Дирилдөө"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Үнсүз"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Тышкы экранга чыгруу"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Үнсүз режимде жеткиликсиз"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml index 0f277f9de292..bc47e5aea38f 100644 --- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Өчүк"</item> <item msgid="578444932039713369">"Күйүк"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Жеткиликсиз"</item> <item msgid="8707481475312432575">"Өчүк"</item> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 1ca3b2fee9bc..db976555009d 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ເພີ່ມວິດເຈັດເພີ່ມເຕີມ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ກົດຄ້າງໄວ້ເພື່ອປັບແຕ່ງວິດເຈັດ"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ປັບແຕ່ງວິດເຈັດ"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ໄອຄອນແອັບສຳລັບວິດເຈັດທີ່ຖືກປິດການນຳໃຊ້"</string> <string name="edit_widget" msgid="9030848101135393954">"ແກ້ໄຂວິດເຈັດ"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"ລຶບອອກ"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ເພີ່ມວິດເຈັດ"</string> @@ -837,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ເມນູເປີດປິດ"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"ໜ້າຈໍລັອກ"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"ທ່ານສາມາດຊອກຫາສະຖານທີ່ຂອງໂທລະສັບເຄື່ອງນີ້ໄດ້ດ້ວຍແອັບຊອກຫາອຸປະກອນຂອງຂ້ອຍເຖິງແມ່ນວ່າຈະປິດເຄື່ອງຢູ່ກໍຕາມ"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"ກຳລັງປິດເຄື່ອງ…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"ຖອດອຸປະກອນຂອງທ່ານອອກ"</string> diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml index d54cf4d24181..75958972cdce 100644 --- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ປິດ"</item> <item msgid="578444932039713369">"ເປີດ"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ບໍ່ສາມາດໃຊ້ໄດ້"</item> <item msgid="8707481475312432575">"ປິດ"</item> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index eac5ee48ead1..9eaab8f6a6f0 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Skambinti"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibruoti"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Nutildyti"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Perdavimas"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nepasiekiama, nes skambutis nutildytas"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Palieskite, kad įjungtumėte garsą."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml index e66f590364dd..94343bac9ff8 100644 --- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Išjungta"</item> <item msgid="578444932039713369">"Įjungta"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nepasiekiama"</item> <item msgid="8707481475312432575">"Išjungta"</item> diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml index d32efec81a99..d8b24676ea27 100644 --- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Izslēgta"</item> <item msgid="578444932039713369">"Ieslēgta"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nav pieejama"</item> <item msgid="8707481475312432575">"Izslēgta"</item> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index b244fe9343bb..6ea4d1f1d9ab 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ѕвони"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрации"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Исклучи звук"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Емитување"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи ѕвонењето е исклучено"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Допрете за да вклучите звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string> diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml index 0a42d7cbe2df..8b0faf7d966e 100644 --- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Исклучено"</item> <item msgid="578444932039713369">"Вклучено"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Недостапно"</item> <item msgid="8707481475312432575">"Исклучено"</item> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 8678b663a089..82d257a473b2 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"കൂടുതൽ വിജറ്റുകൾ ചേർക്കുക"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കാൻ ദീർഘനേരം അമർത്തുക"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കുക"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"പ്രവർത്തനരഹിതമാക്കിയ വിജറ്റിനുള്ള ആപ്പ് ഐക്കൺ"</string> <string name="edit_widget" msgid="9030848101135393954">"വിജറ്റ് എഡിറ്റ് ചെയ്യുക"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"നീക്കം ചെയ്യുക"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"വിജറ്റ് ചേർക്കുക"</string> @@ -837,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"പവർ മെനു"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"പേജ് <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"ലോക്ക് സ്ക്രീൻ"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"ഓഫായിരിക്കുമ്പോഴും Find My Device ഉപയോഗിച്ച് നിങ്ങൾക്ക് ഈ ഫോൺ കണ്ടെത്താനാകും"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"ഷട്ട്ഡൗൺ ചെയ്യുന്നു…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"ഉപകരണം അൺപ്ലഗ് ചെയ്യുക"</string> diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml index 62bac5cd1212..529d0def3e27 100644 --- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ഓഫാണ്"</item> <item msgid="578444932039713369">"ഓണാണ്"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ലഭ്യമല്ല"</item> <item msgid="8707481475312432575">"ഓഫാണ്"</item> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 8aafee3341de..aad6a13c43d7 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Хонх дуугаргах"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Чичиргэх"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Хаах"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Дамжуулах"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Хонхны дууг хаасан тул боломжгүй"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дууг нь нээхийн тулд товшино уу."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml index 33f359672477..0db122904755 100644 --- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Унтраалттай"</item> <item msgid="578444932039713369">"Асаалттай"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Боломжгүй"</item> <item msgid="8707481475312432575">"Унтраалттай"</item> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index e781b45803ef..d7acf5f5fea2 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"रिंग करा"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"व्हायब्रेट"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"म्यूट करा"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"कास्ट करा"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट केल्यामुळे उपलब्ध नाही"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करण्यासाठी टॅप करा."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string> diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml index 24d3b47af961..b70a5cc671a7 100644 --- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"बंद आहे"</item> <item msgid="578444932039713369">"सुरू आहे"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"उपलब्ध नाही"</item> <item msgid="8707481475312432575">"बंद आहे"</item> diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml index 07a8426feeb2..b72a3756ccaa 100644 --- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Mati"</item> <item msgid="578444932039713369">"Hidup"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Tidak tersedia"</item> <item msgid="8707481475312432575">"Mati"</item> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index c408118e48ce..c6d46bccf254 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"အသံမြည်သည်"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"တုန်ခါသည်"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"အသံတိတ်သည်"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"ကာစ်လုပ်ရန်"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"ဖုန်းမြည်သံပိတ်ထားသဖြင့် မရနိုင်ပါ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml index fd375d4f3eb1..d223dc9d0bed 100644 --- a/packages/SystemUI/res/values-my/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ပိတ်"</item> <item msgid="578444932039713369">"ဖွင့်"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"မရနိုင်ပါ"</item> <item msgid="8707481475312432575">"ပိတ်"</item> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 2ce016ac586f..a2b97eee2fbe 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrer"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ignorer"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Utilgjengelig fordi ringelyden er kuttet"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trykk for å slå på lyden."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml index e4a811941b2a..2ed00960fdfd 100644 --- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Av"</item> <item msgid="578444932039713369">"På"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Utilgjengelig"</item> <item msgid="8707481475312432575">"Av"</item> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 0096f480f67e..1388a85a6f15 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"घन्टी"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"कम्पन"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"म्युट गर्नुहोस्"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"कास्ट गर्नुहोस्"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"डिभाइस म्युट गरिएकाले यो सुविधा उपलब्ध छैन"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। अनम्यूट गर्नाका लागि ट्याप गर्नुहोस्।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। कम्पनमा सेट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। म्यूट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml index 5cf91e533494..40159fb17279 100644 --- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"अफ छ"</item> <item msgid="578444932039713369">"अन छ"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"उपलब्ध छैन"</item> <item msgid="8707481475312432575">"अफ छ"</item> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 723b9096cfcf..09156cf074e6 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bellen"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Trillen"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Geluid staat uit"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Casten"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar, belgeluid staat uit"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml index 592ecf5b69d6..60e35da7611b 100644 --- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Uit"</item> <item msgid="578444932039713369">"Aan"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Niet beschikbaar"</item> <item msgid="8707481475312432575">"Uit"</item> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 612e012a2546..b49c2979ac43 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -287,7 +287,7 @@ <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ମିଡିଆ ଡିଭାଇସ୍"</string> <string name="quick_settings_user_title" msgid="8673045967216204537">"ୟୁଜର"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string> - <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟରନେଟ"</string> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟର୍ନେଟ"</string> <string name="quick_settings_networks_available" msgid="1875138606855420438">"ନେଟୱାର୍କଗୁଡ଼ିକ ଉପଲବ୍ଧ"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"କୌଣସି ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string> @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"ରିଙ୍ଗ"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ଭାଇବ୍ରେଟ୍"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"ମ୍ୟୁଟ"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"କାଷ୍ଟ କରନ୍ତୁ"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"ରିଂକୁ ମ୍ୟୁଟ କରାଯାଇଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ଅନମ୍ୟୁଟ୍ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ଭାଇବ୍ରେଟ୍ ସେଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ମ୍ୟୁଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml index d362c65fa971..43bddbf9f49b 100644 --- a/packages/SystemUI/res/values-or/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ବନ୍ଦ ଅଛି"</item> <item msgid="578444932039713369">"ଚାଲୁ ଅଛି"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ଉପଲବ୍ଧ ନାହିଁ"</item> <item msgid="8707481475312432575">"ବନ୍ଦ ଅଛି"</item> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index c02641329304..cd55198317e8 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"ਘੰਟੀ"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ਥਰਥਰਾਹਟ"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"ਮਿਊਟ"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"ਕਾਸਟ ਕਰੋ"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਘੰਟੀ ਮਿਊਟ ਕੀਤੀ ਹੋਈ ਹੈ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml index f249afb72b6d..5f0ca172dca9 100644 --- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ਬੰਦ ਹੈ"</item> <item msgid="578444932039713369">"ਚਾਲੂ ਹੈ"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ਅਣਉਪਲਬਧ ਹੈ"</item> <item msgid="8707481475312432575">"ਬੰਦ ਹੈ"</item> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 44639b990e48..76547ed5266c 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Dzwonek"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Wibracje"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Wyciszenie"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Przesyłanie"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niedostępne, bo dzwonek jest wyciszony"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Kliknij, by wyłączyć wyciszenie."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string> diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml index ea6ad58c825a..5e3a1189c940 100644 --- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Wyłączony"</item> <item msgid="578444932039713369">"Włączony"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Niedostępne"</item> <item msgid="8707481475312432575">"Wyłączone"</item> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 639a488c5226..598ef78ae63b 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Tocar"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Desativar som"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Transmitir"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque foi silenciado"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml index 28c07f40b6b9..d4fd83803ba7 100644 --- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desativada"</item> <item msgid="578444932039713369">"Ativada"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Indisponível"</item> <item msgid="8707481475312432575">"Desativado"</item> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index f34dea1dfbf2..7e5a7368faaf 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicionar mais widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha premido para personalizar os widgets"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícone da app do widget desativado"</string> <string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Toque"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Desativar som"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Transmitir"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível porque o toque está desat."</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para reativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu ligar/desligar"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"Ecrã de bloqueio"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"Pode localizar este telemóvel com o serviço Localizar o meu dispositivo mesmo quando está desligado"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"A encerrar…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Veja os passos de manutenção"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Veja os passos de manutenção"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desligue o dispositivo"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml index b58b8488a3e4..e94b1ec6b0fd 100644 --- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desligado"</item> <item msgid="578444932039713369">"Ligado"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Indisponível"</item> <item msgid="8707481475312432575">"Desligado"</item> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 639a488c5226..598ef78ae63b 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Tocar"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Desativar som"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Transmitir"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque foi silenciado"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml index 28c07f40b6b9..d4fd83803ba7 100644 --- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Desativada"</item> <item msgid="578444932039713369">"Ativada"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Indisponível"</item> <item msgid="8707481475312432575">"Desativado"</item> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 7d8c2a31751c..026e22bd6328 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonerie"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrații"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Blochează"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Proiectează"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponibil; soneria este dezactivată"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml index 5a5eb9f6788c..75565f942ac4 100644 --- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Dezactivată"</item> <item msgid="578444932039713369">"Activată"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Indisponibilă"</item> <item msgid="8707481475312432575">"Dezactivată"</item> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 37b7ae5f7b3d..1df112ba8493 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Со звуком"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрация"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Без звука"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Трансляция"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно, когда отключен звук вызовов"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Нажмите, чтобы включить звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string> diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml index cd140791bbd5..3099e008c93a 100644 --- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Откл."</item> <item msgid="578444932039713369">"Вкл."</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Функция недоступна"</item> <item msgid="8707481475312432575">"Откл."</item> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index c851f8583f0d..f4b7d1eb1254 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"නාද කරන්න"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"කම්පනය කරන්න"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"නිහඬ කරන්න"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"විකාශය"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"නාදය නිහඬ කර ඇති නිසා නොලැබේ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml index fcd768bfc4e3..48e8cc4af507 100644 --- a/packages/SystemUI/res/values-si/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"අක්රියයි"</item> <item msgid="578444932039713369">"සක්රියයි"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"නොමැත"</item> <item msgid="8707481475312432575">"අක්රියයි"</item> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index d25e3687b589..0f579da44ed6 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Prezvoniť"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrovať"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Vypnúť zvuk"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Prenos"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, pretože je vypnuté zvonenie"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnite zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string> diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml index 660f85d365e2..fdfcd27db263 100644 --- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Vypnuté"</item> <item msgid="578444932039713369">"Zapnuté"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nie je k dispozícii"</item> <item msgid="8707481475312432575">"Vypnuté"</item> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index eb56a44aa313..7acaa54cbbad 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvonjenje"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Utišano"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Predvajanje"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ni na voljo, ker je zvonjenje izklopljeno"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dotaknite se, če želite vklopiti zvok."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za dostopnost bo morda izklopljen zvok."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za dostopnost bo morda izklopljen zvok."</string> diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml index d7e62caac28d..3804d639bb52 100644 --- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Izklopljeno"</item> <item msgid="578444932039713369">"Vklopljeno"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Ni na voljo"</item> <item msgid="8707481475312432575">"Izklopljeno"</item> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index eb8fa4372d2e..21a974faf29e 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bjeri ziles"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dridhje"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Pa zë"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Transmeto"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nuk ofrohet; ziles i është hequr zëri"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trokit për të aktivizuar."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string> diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml index b8e1355dfb25..6318700bf6f2 100644 --- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Joaktive"</item> <item msgid="578444932039713369">"Aktiv"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Nuk ofrohet"</item> <item msgid="8707481475312432575">"Joaktive"</item> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 0644a8095675..2f52f90b3622 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Активирај звоно"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрирај"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Искључи звук"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Пребацивање"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно јер је звук искључен"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Додирните да бисте укључили звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string> diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml index c959bfb0eb73..e4cf0b6cd2c0 100644 --- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Искључено"</item> <item msgid="578444932039713369">"Укључено"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Недоступно"</item> <item msgid="8707481475312432575">"Искључено"</item> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 208891f4fbfa..20bc7e70a0da 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ringsignal"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Dölj"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Casta"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Otillgängligt eftersom ringljudet är av"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryck här om du vill slå på ljudet."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string> diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml index 28717dfa5229..8981ac775e50 100644 --- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Av"</item> <item msgid="578444932039713369">"På"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Inte tillgängligt"</item> <item msgid="8707481475312432575">"Av"</item> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 404cb487e445..fa0e7b5eba05 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Piga"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Kutetema"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Zima sauti"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Tuma"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Halipatikani kwa sababu sauti imezimwa"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Gusa ili urejeshe."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml index 2fe40603bc7d..08a1f149264c 100644 --- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Kimezimwa"</item> <item msgid="578444932039713369">"Kimewashwa"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Hakipatikani"</item> <item msgid="8707481475312432575">"Kimezimwa"</item> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 03dd362e2b18..0f360e6bc17b 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"ஒலி"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"அதிர்வு"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"அமைதி"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"அலைபரப்பு"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"\'ரிங்\' மியூட்டில் உள்ளதால் கிடைக்கவில்லை"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ஒலி இயக்க, தட்டவும்."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml index 5bcc6c761e1c..741d6dea191f 100644 --- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"முடக்கப்பட்டுள்ளது"</item> <item msgid="578444932039713369">"இயக்கப்பட்டுள்ளது"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"கிடைக்கவில்லை"</item> <item msgid="8707481475312432575">"முடக்கப்பட்டுள்ளது"</item> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 08067c93bead..4e8e2d0e4ca8 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"రింగ్"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"వైబ్రేట్"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"మ్యూట్"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"ప్రసారం చేయండి"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"వాల్యూమ్ మ్యూట్ అయినందున అందుబాటులో లేదు"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. అన్మ్యూట్ చేయడానికి నొక్కండి."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. వైబ్రేషన్కు సెట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml index 9d2b407ccd10..6ff293479860 100644 --- a/packages/SystemUI/res/values-te/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ఆఫ్లో ఉంది"</item> <item msgid="578444932039713369">"ఆన్లో ఉంది"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"అందుబాటులో లేదు"</item> <item msgid="8707481475312432575">"ఆఫ్లో ఉంది"</item> diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml index 69449a72196e..d961385ba72e 100644 --- a/packages/SystemUI/res/values-th/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"ปิด"</item> <item msgid="578444932039713369">"เปิด"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"ไม่พร้อมใช้งาน"</item> <item msgid="8707481475312432575">"ปิด"</item> diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml index 689c2a26ca61..a12c010f766a 100644 --- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Naka-off"</item> <item msgid="578444932039713369">"Naka-on"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Hindi available"</item> <item msgid="8707481475312432575">"Naka-off"</item> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 495000ba0318..2cdc6e7e1296 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zili çaldır"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Titreşim"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Sesi kapat"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Yayınla"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zil sesi kapatıldığı için kullanılamıyor"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sesi açmak için dokunun."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Titreşime ayarlamak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml index a8c7f784920d..c6a8aecf0da1 100644 --- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Kapalı"</item> <item msgid="578444932039713369">"Açık"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Kullanılamıyor"</item> <item msgid="8707481475312432575">"Kapalı"</item> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 67d5ffc3373a..c89ae75a5ae2 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Дзвінок"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вібросигнал"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Без звуку"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Трансляція"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно: звук дзвінків вимкнено"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Торкніться, щоб увімкнути звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string> diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml index 4062f1be4c08..a8e1ab8f3f99 100644 --- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Вимкнено"</item> <item msgid="578444932039713369">"Увімкнено"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Недоступно"</item> <item msgid="8707481475312432575">"Вимкнено"</item> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 7414ac62c4a8..3237f32f5347 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -429,8 +429,7 @@ <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"مزید ویجٹس شامل کریں"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ویجٹس کو حسب ضرورت بنانے کے لیے لانگ پریس کریں"</string> <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ویجیٹس کو حسب ضرورت بنائیں"</string> - <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) --> - <skip /> + <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"غیر فعال ویجیٹ کے لئے ایپ آئیکن"</string> <string name="edit_widget" msgid="9030848101135393954">"ویجیٹ میں ترمیم کریں"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"ہٹائیں"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ویجیٹ شامل کریں"</string> @@ -575,10 +574,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"رِنگ کریں"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"وائبریٹ"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"خاموش کریں"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"کاسٹ"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"دستیاب نہیں ہے کیونکہ رنگ خاموش ہے"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> @@ -839,10 +836,8 @@ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"پاور مینیو"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحہ <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"مقفل اسکرین"</string> - <!-- no translation found for finder_active (7907846989716941952) --> - <skip /> - <!-- no translation found for shutdown_progress (5464239146561542178) --> - <skip /> + <string name="finder_active" msgid="7907846989716941952">"پاور آف ہونے پر بھی آپ میرا آلہ ڈھونڈیں کے ساتھ اس فون کو تلاش کر سکتے ہیں"</string> + <string name="shutdown_progress" msgid="5464239146561542178">"بند ہو رہا ہے…"</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"نگہداشت کے اقدامات ملاحظہ کریں"</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"نگہداشت کے اقدامات ملاحظہ کریں"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"اپنے آلہ کو ان پلگ کریں"</string> diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml index bb27b9fbe9f2..6d1e7076da40 100644 --- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"آف ہے"</item> <item msgid="578444932039713369">"آن ہے"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"دستیاب نہیں ہے"</item> <item msgid="8707481475312432575">"آف ہے"</item> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 09e5ff04d9b6..478fcdb95527 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Jiringlatish"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Tebranish"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ovozsiz"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Translatsiya"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Jiringlash ovozsizligi uchun ishlamaydi"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ovozini yoqish uchun ustiga bosing."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tebranishni yoqish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string> diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml index bd5ee8951c5b..0558c4a91794 100644 --- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Oʻchiq"</item> <item msgid="578444932039713369">"Yoniq"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Ishlamaydi"</item> <item msgid="8707481475312432575">"Oʻchiq"</item> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index f7057e948b70..7e8638b5bdeb 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Đổ chuông"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Rung"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Tắt tiếng"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Truyền"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Không hoạt động vì chuông bị tắt"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Nhấn để bật tiếng."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml index 201a45b3d445..eee10d330258 100644 --- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Đang tắt"</item> <item msgid="578444932039713369">"Đang bật"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Không hoạt động"</item> <item msgid="8707481475312432575">"Đang tắt"</item> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 2c87e2491814..255213858f91 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"响铃"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"振动"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"静音"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"投屏"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。点按即可取消静音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml index 3ab2d7a3d1c7..82ab6710729d 100644 --- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"已关闭"</item> <item msgid="578444932039713369">"已开启"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"不可用"</item> <item msgid="8707481475312432575">"已关闭"</item> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 78d7c7a0c9b4..5941dad6903b 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"鈴聲"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"震動"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"靜音"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"投放"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設定為靜音,因此無法使用"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕按即可取消靜音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml index 89d66284ede6..6bac27502583 100644 --- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"已關閉"</item> <item msgid="578444932039713369">"已開啟"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"無法使用"</item> <item msgid="8707481475312432575">"已關閉"</item> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 46d775086fc3..c46d831c741a 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"鈴聲"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"震動"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"靜音"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"投放"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設為靜音,因此無法使用"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕觸即可取消靜音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml index a046e3354729..5794bf1703f9 100644 --- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"已關閉"</item> <item msgid="578444932039713369">"已開啟"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"無法使用"</item> <item msgid="8707481475312432575">"已關閉"</item> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index f12c2bbdd81c..0bbac05638cc 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -575,10 +575,8 @@ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Khalisa"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dlidlizela"</string> <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Thulisa"</string> - <!-- no translation found for media_device_cast (4786241789687569892) --> - <skip /> - <!-- no translation found for stream_notification_unavailable (4313854556205836435) --> - <skip /> + <string name="media_device_cast" msgid="4786241789687569892">"Sakaza"</string> + <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ayitholakali ngoba ukukhala kuthulisiwe"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Thepha ukuze ususe ukuthula."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string> diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml index e35840b85ab4..8c7b652cdfe0 100644 --- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml @@ -126,6 +126,9 @@ <item msgid="8259411607272330225">"Valiwe"</item> <item msgid="578444932039713369">"Vuliwe"</item> </string-array> + <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) --> + <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) --> + <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) --> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"Akutholakali"</item> <item msgid="8707481475312432575">"Valiwe"</item> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 035cfdc492d0..71ae0d716429 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -223,7 +223,6 @@ <item type="id" name="lock_icon" /> <item type="id" name="lock_icon_bg" /> <item type="id" name="burn_in_layer" /> - <item type="id" name="burn_in_layer_empty_view" /> <item type="id" name="communal_tutorial_indicator" /> <item type="id" name="nssl_placeholder_barrier_bottom" /> <item type="id" name="ambient_indication_container" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 346bdfc8b2d8..b71341791ee6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3254,6 +3254,12 @@ <!-- 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> + <!-- QuickSettings: Additional label for the auto-rotation quicksettings tile indicating that the setting corresponds to the folded posture for a foldable device [CHAR LIMIT=32] --> + <string name="quick_settings_rotation_posture_folded">folded</string> + <!-- QuickSettings: Additional label for the auto-rotation quicksettings tile indicating that the setting corresponds to the unfolded posture for a foldable device [CHAR LIMIT=32] --> + <string name="quick_settings_rotation_posture_unfolded">unfolded</string> + <!-- QuickSettings: template for rotation tile foldable secondary label [CHAR LIMIT=64] !--> + <string name="rotation_tile_with_posture_secondary_label_template">%1$s / %2$s</string> <!-- Title for notification of low stylus battery with percentage. "percentage" is the value of the battery capacity remaining [CHAR LIMIT=none]--> <string name="stylus_battery_low_percentage"><xliff:g id="percentage" example="16%">%s</xliff:g> battery remaining</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 617eadbd447c..ce08ca3e43af 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1581,4 +1581,8 @@ <style name="Theme.PrivacyDialog" parent="@style/Theme.SystemUI.Dialog"> <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item> </style> + + <style name="Theme.SystemUI.Dialog.StickyKeys" parent="@style/Theme.SystemUI.Dialog"> + <item name="android:colorBackground">@color/transparent</item> + </style> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java index 75cace424e8b..b9b8fbe9637f 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java @@ -209,6 +209,9 @@ public class CarrierTextManager { // This will set/remove the listeners appropriately. Note that it will never double // add the listeners. handleSetListening(mCarrierTextCallback); + mainExecutor.execute(() -> { + mKeyguardUpdateMonitor.registerCallback(mCallback); + }); } }); } @@ -276,7 +279,6 @@ public class CarrierTextManager { if (mNetworkSupported.get()) { // Keyguard update monitor expects callbacks from main thread mMainExecutor.execute(() -> { - mKeyguardUpdateMonitor.registerCallback(mCallback); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); }); mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener); @@ -289,7 +291,6 @@ public class CarrierTextManager { } else { mCarrierTextCallback = null; mMainExecutor.execute(() -> { - mKeyguardUpdateMonitor.removeCallback(mCallback); mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); }); mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index c0ae4a1f4036..9421f150a38a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -53,6 +53,9 @@ import com.android.systemui.Dumpable; import com.android.systemui.animation.ViewHierarchyAnimator; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.TransitionState; +import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.plugins.clocks.ClockController; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.shared.model.ScreenPowerState; @@ -101,6 +104,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final Rect mClipBounds = new Rect(); private final KeyguardInteractor mKeyguardInteractor; private final PowerInteractor mPowerInteractor; + private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final DozeParameters mDozeParameters; private View mStatusArea = null; @@ -108,6 +112,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private Boolean mSplitShadeEnabled = false; private Boolean mStatusViewCentered = true; + private boolean mGoneToAodTransitionRunning = false; private DumpManager mDumpManager; private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener = @@ -176,6 +181,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV KeyguardLogger logger, InteractionJankMonitor interactionJankMonitor, KeyguardInteractor keyguardInteractor, + KeyguardTransitionInteractor keyguardTransitionInteractor, DumpManager dumpManager, PowerInteractor powerInteractor) { super(keyguardStatusView); @@ -191,6 +197,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mDumpManager = dumpManager; mKeyguardInteractor = keyguardInteractor; mPowerInteractor = powerInteractor; + mKeyguardTransitionInteractor = keyguardTransitionInteractor; } @Override @@ -225,7 +232,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mDumpManager.registerDumpable(getInstanceName(), this); if (migrateClocksToBlueprint()) { startCoroutines(EmptyCoroutineContext.INSTANCE); - mView.setVisibility(View.GONE); } } @@ -241,6 +247,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV dozeTimeTick(); } }, context); + + collectFlow(mView, mKeyguardTransitionInteractor.getGoneToAodTransition(), + (TransitionStep step) -> { + if (step.getTransitionState() == TransitionState.RUNNING) { + mGoneToAodTransitionRunning = true; + } else { + mGoneToAodTransitionRunning = false; + } + }, context); } public KeyguardStatusView getView() { @@ -311,7 +326,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * Set keyguard status view alpha. */ public void setAlpha(float alpha) { - if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) { + if (!mKeyguardVisibilityHelper.isVisibilityAnimating() && !mGoneToAodTransitionRunning) { mView.setAlpha(alpha); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index b7667a8669f4..8c51a4e0ce66 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3313,6 +3313,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab becameAbsent |= ABSENT_SIM_STATE_LIST.contains(state); + // TODO(b/327476182): Preserve SIM_STATE_CARD_IO_ERROR sims in a separate data source. SimData data = mSimDatas.get(subId); final boolean changed; if (data == null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 77054bd29147..2000028dff41 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -88,10 +88,6 @@ public class KeyguardVisibilityHelper { boolean keyguardFadingAway, boolean goingToFullShade, int oldStatusBarState) { - if (migrateClocksToBlueprint()) { - log("Ignoring all of KeyguardVisibilityelper"); - return; - } Assert.isMainThread(); PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA); boolean isOccluded = mKeyguardStateController.isOccluded(); diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt new file mode 100644 index 000000000000..f12382801887 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera + +import com.android.systemui.camera.data.repository.CameraAutoRotateRepository +import com.android.systemui.camera.data.repository.CameraAutoRotateRepositoryImpl +import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepository +import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepositoryImpl +import dagger.Binds +import dagger.Module + +/** Module for repositories that provide data regarding camera rotation state. */ +@Module +interface CameraRotationModule { + + @Binds + fun bindsPrivacyRepoImpl(impl: CameraSensorPrivacyRepositoryImpl): CameraSensorPrivacyRepository + @Binds fun bindsRotateRepoImpl(impl: CameraAutoRotateRepositoryImpl): CameraAutoRotateRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt new file mode 100644 index 000000000000..023fd285c6d8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.os.UserHandle +import android.provider.Settings +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +interface CameraAutoRotateRepository { + /** @return true if camera auto rotate setting is enabled */ + fun isCameraAutoRotateSettingEnabled(userHandle: UserHandle): StateFlow<Boolean> +} + +@SysUISingleton +class CameraAutoRotateRepositoryImpl +@Inject +constructor( + private val secureSettings: SecureSettings, + @Background private val bgCoroutineContext: CoroutineContext, + @Application private val applicationScope: CoroutineScope, +) : CameraAutoRotateRepository { + private val userMap = mutableMapOf<Int, StateFlow<Boolean>>() + + override fun isCameraAutoRotateSettingEnabled(userHandle: UserHandle): StateFlow<Boolean> { + return userMap.getOrPut(userHandle.identifier) { + secureSettings + .observerFlow(userHandle.identifier, Settings.Secure.CAMERA_AUTOROTATE) + .map { isAutoRotateSettingEnabled(userHandle.identifier) } + .onStart { emit(isAutoRotateSettingEnabled(userHandle.identifier)) } + .flowOn(bgCoroutineContext) + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false) + } + } + + private fun isAutoRotateSettingEnabled(userId: Int) = + secureSettings.getIntForUser(SETTING_NAME, DISABLED, userId) == ENABLED + + private companion object { + const val SETTING_NAME = Settings.Secure.CAMERA_AUTOROTATE + const val DISABLED = 0 + const val ENABLED = 1 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt new file mode 100644 index 000000000000..7816a1487c01 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.hardware.SensorPrivacyManager +import android.hardware.SensorPrivacyManager.Sensors.CAMERA +import android.os.UserHandle +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +interface CameraSensorPrivacyRepository { + /** Tracks whether camera sensor privacy is enabled. */ + fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> +} + +@SysUISingleton +class CameraSensorPrivacyRepositoryImpl +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + @Application private val scope: CoroutineScope, + private val privacyManager: SensorPrivacyManager, +) : CameraSensorPrivacyRepository { + private val userMap = mutableMapOf<Int, StateFlow<Boolean>>() + + /** Whether camera sensor privacy is enabled */ + override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> = + userMap.getOrPut(userHandle.identifier) { + privacyManager + .isEnabled(userHandle) + .flowOn(bgCoroutineContext) + .stateIn(scope, SharingStarted.WhileSubscribed(), false) + } +} + +fun SensorPrivacyManager.isEnabled(userHandle: UserHandle): Flow<Boolean> { + return conflatedCallbackFlow { + val privacyCallback = + SensorPrivacyManager.OnSensorPrivacyChangedListener { sensor, enabled -> + if (sensor == CAMERA) { + trySend(enabled) + } + } + addSensorPrivacyListener(CAMERA, userHandle.identifier, privacyCallback) + awaitClose { removeSensorPrivacyListener(privacyCallback) } + } + .onStart { emit(isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, CAMERA)) } + .distinctUntilChanged() +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 8397372e0735..c3c7411c401d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -16,25 +16,23 @@ package com.android.systemui.communal +import com.android.compose.animation.scene.SceneKey import com.android.systemui.CoreStartable import com.android.systemui.communal.domain.interactor.CommunalInteractor -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dock.DockManager -import com.android.systemui.dock.retrieveIsDocked import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest @@ -65,38 +63,40 @@ constructor( .onEach { nextScene -> communalInteractor.onSceneChanged(nextScene) } .launchIn(applicationScope) + // TODO(b/322787129): re-enable once custom animations are in place // Handle automatically switching to communal when docked. - dockManager - .retrieveIsDocked() - // Allow some time after docking to ensure the dream doesn't start. If the dream - // starts, then we don't want to automatically transition to glanceable hub. - .debounce(DOCK_DEBOUNCE_DELAY) - .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair) - .onEach { (docked, lastStartedState) -> - if (docked && lastStartedState == KeyguardState.LOCKSCREEN) { - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) - } - } - .launchIn(bgScope) + // dockManager + // .retrieveIsDocked() + // // Allow some time after docking to ensure the dream doesn't start. If the + // dream + // // starts, then we don't want to automatically transition to glanceable hub. + // .debounce(DOCK_DEBOUNCE_DELAY) + // .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair) + // .onEach { (docked, lastStartedState) -> + // if (docked && lastStartedState == KeyguardState.LOCKSCREEN) { + // communalInteractor.onSceneChanged(CommunalScenes.Communal) + // } + // } + // .launchIn(bgScope) } private suspend fun determineSceneAfterTransition( lastStartedTransition: TransitionStep, - ): CommunalSceneKey? { + ): SceneKey? { val to = lastStartedTransition.to val from = lastStartedTransition.from val docked = dockManager.isDocked return when { - docked && to == KeyguardState.LOCKSCREEN && from != KeyguardState.GLANCEABLE_HUB -> { - CommunalSceneKey.Communal + docked && to == KeyguardState.LOCKSCREEN && from == KeyguardState.DREAMING -> { + CommunalScenes.Communal } - to == KeyguardState.GONE -> CommunalSceneKey.Blank + to == KeyguardState.GONE -> CommunalScenes.Blank !docked && !KeyguardState.deviceIsAwakeInState(to) -> { // If the user taps the screen and wakes the device within this timeout, we don't // want to dismiss the hub delay(AWAKE_DEBOUNCE_DELAY) - CommunalSceneKey.Blank + CommunalScenes.Blank } else -> null } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt index f4a3bcb7a0fa..201ce832cc41 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt @@ -16,8 +16,9 @@ package com.android.systemui.communal.data.repository -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.scene.data.repository.SceneContainerRepository @@ -40,20 +41,20 @@ interface CommunalRepository { * Target scene as requested by the underlying [SceneTransitionLayout] or through * [setDesiredScene]. */ - val desiredScene: StateFlow<CommunalSceneKey> + val desiredScene: StateFlow<SceneKey> /** Exposes the transition state of the communal [SceneTransitionLayout]. */ - val transitionState: StateFlow<ObservableCommunalTransitionState> + val transitionState: StateFlow<ObservableTransitionState> /** Updates the requested scene. */ - fun setDesiredScene(desiredScene: CommunalSceneKey) + fun setDesiredScene(desiredScene: SceneKey) /** * Updates the transition state of the hub [SceneTransitionLayout]. * * Note that you must call is with `null` when the UI is done or risk a memory leak. */ - fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) + fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) } @OptIn(ExperimentalCoroutinesApi::class) @@ -66,14 +67,12 @@ constructor( sceneContainerRepository: SceneContainerRepository, ) : CommunalRepository { - private val _desiredScene: MutableStateFlow<CommunalSceneKey> = - MutableStateFlow(CommunalSceneKey.DEFAULT) - override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow() + private val _desiredScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default) + override val desiredScene: StateFlow<SceneKey> = _desiredScene.asStateFlow() - private val defaultTransitionState = - ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT) - private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null) - override val transitionState: StateFlow<ObservableCommunalTransitionState> = + private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) + private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null) + override val transitionState: StateFlow<ObservableTransitionState> = _transitionState .flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) } .stateIn( @@ -82,7 +81,7 @@ constructor( initialValue = defaultTransitionState, ) - override fun setDesiredScene(desiredScene: CommunalSceneKey) { + override fun setDesiredScene(desiredScene: SceneKey) { _desiredScene.value = desiredScene } @@ -91,7 +90,7 @@ constructor( * * Note that you must call is with `null` when the UI is done or risk a memory leak. */ - override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) { + override fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { _transitionState.value = transitionState } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 151e1eeaefc5..814295787b6c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -19,6 +19,8 @@ package com.android.systemui.communal.domain.interactor import android.app.smartspace.SmartspaceTarget import android.content.ComponentName import android.os.UserHandle +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.communal.data.repository.CommunalMediaRepository import com.android.systemui.communal.data.repository.CommunalPrefsRepository import com.android.systemui.communal.data.repository.CommunalRepository @@ -29,9 +31,8 @@ import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.shared.model.CommunalContentSize.FULL import com.android.systemui.communal.shared.model.CommunalContentSize.HALF import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.WidgetConfigurator @@ -46,7 +47,7 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.data.repository.SmartspaceRepository import com.android.systemui.util.kotlin.BooleanFlowOperators.and @@ -131,34 +132,33 @@ constructor( * Target scene as requested by the underlying [SceneTransitionLayout] or through * [onSceneChanged]. * - * If [isCommunalAvailable] is false, will return [CommunalSceneKey.Blank] + * If [isCommunalAvailable] is false, will return [CommunalScenes.Blank] */ - val desiredScene: Flow<CommunalSceneKey> = + val desiredScene: Flow<SceneKey> = communalRepository.desiredScene.combine(isCommunalAvailable) { scene, available -> - if (available) scene else CommunalSceneKey.Blank + if (available) scene else CommunalScenes.Blank } /** Transition state of the hub mode. */ - val transitionState: StateFlow<ObservableCommunalTransitionState> = - communalRepository.transitionState + val transitionState: StateFlow<ObservableTransitionState> = communalRepository.transitionState /** * Updates the transition state of the hub [SceneTransitionLayout]. * * Note that you must call is with `null` when the UI is done or risk a memory leak. */ - fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) { + fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { communalRepository.setTransitionState(transitionState) } /** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */ - fun transitionProgressToScene(targetScene: CommunalSceneKey) = + fun transitionProgressToScene(targetScene: SceneKey) = transitionState .flatMapLatest { state -> when (state) { - is ObservableCommunalTransitionState.Idle -> + is ObservableTransitionState.Idle -> flowOf(CommunalTransitionProgress.Idle(state.scene)) - is ObservableCommunalTransitionState.Transition -> + is ObservableTransitionState.Transition -> if (state.toScene == targetScene) { state.progress.map { CommunalTransitionProgress.Transition( @@ -176,7 +176,7 @@ constructor( /** * Flow that emits a boolean if the communal UI is the target scene, ie. the [desiredScene] is - * the [CommunalSceneKey.Communal]. + * the [CommunalScenes.Communal]. * * This will be true as soon as the desired scene is set programmatically or at whatever point * during a fling that SceneTransitionLayout determines that the end state will be the communal @@ -191,9 +191,9 @@ constructor( flow { emit(sceneContainerFlags.isEnabled()) } .flatMapLatest { sceneContainerEnabled -> if (sceneContainerEnabled) { - sceneInteractor.currentScene.map { it == SceneKey.Communal } + sceneInteractor.currentScene.map { it == Scenes.Communal } } else { - desiredScene.map { it == CommunalSceneKey.Communal } + desiredScene.map { it == CommunalScenes.Communal } } } .distinctUntilChanged() @@ -220,7 +220,7 @@ constructor( */ val isIdleOnCommunal: Flow<Boolean> = communalRepository.transitionState.map { - it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Communal + it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Communal } /** @@ -230,11 +230,11 @@ constructor( */ val isCommunalVisible: Flow<Boolean> = communalRepository.transitionState.map { - !(it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Blank) + !(it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Blank) } /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */ - fun onSceneChanged(newScene: CommunalSceneKey) { + fun onSceneChanged(newScene: SceneKey) { communalRepository.setDesiredScene(newScene) } @@ -422,7 +422,7 @@ constructor( /** Simplified transition progress data class for tracking a single transition between scenes. */ sealed class CommunalTransitionProgress { /** No transition/animation is currently running. */ - data class Idle(val scene: CommunalSceneKey) : CommunalTransitionProgress() + data class Idle(val scene: SceneKey) : CommunalTransitionProgress() /** There is a transition animating to the expected scene. */ data class Transition( diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt index 889023e8dab6..f2b473864a78 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt @@ -16,12 +16,12 @@ package com.android.systemui.communal.log +import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.UiEventLogger import com.android.systemui.CoreStartable import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.shared.log.CommunalUiEvent -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.kotlin.pairwise @@ -87,25 +87,25 @@ constructor( } /** Whether currently in communal scene. */ -private fun ObservableCommunalTransitionState.isOnCommunal(): Boolean { - return this is ObservableCommunalTransitionState.Idle && scene == CommunalSceneKey.Communal +private fun ObservableTransitionState.isOnCommunal(): Boolean { + return this is ObservableTransitionState.Idle && scene == CommunalScenes.Communal } /** Whether currently in a scene other than communal. */ -private fun ObservableCommunalTransitionState.isNotOnCommunal(): Boolean { - return this is ObservableCommunalTransitionState.Idle && scene != CommunalSceneKey.Communal +private fun ObservableTransitionState.isNotOnCommunal(): Boolean { + return this is ObservableTransitionState.Idle && scene != CommunalScenes.Communal } /** Whether currently transitioning from another scene to communal. */ -private fun ObservableCommunalTransitionState.isSwipingToCommunal(): Boolean { - return this is ObservableCommunalTransitionState.Transition && - toScene == CommunalSceneKey.Communal && +private fun ObservableTransitionState.isSwipingToCommunal(): Boolean { + return this is ObservableTransitionState.Transition && + toScene == CommunalScenes.Communal && isInitiatedByUserInput } /** Whether currently transitioning from communal to another scene. */ -private fun ObservableCommunalTransitionState.isSwipingFromCommunal(): Boolean { - return this is ObservableCommunalTransitionState.Transition && - fromScene == CommunalSceneKey.Communal && +private fun ObservableTransitionState.isSwipingFromCommunal(): Boolean { + return this is ObservableTransitionState.Transition && + fromScene == CommunalScenes.Communal && isInitiatedByUserInput } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalScenes.kt index c68dd4ff271c..d5a56c1e9ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalScenes.kt @@ -16,21 +16,15 @@ package com.android.systemui.communal.shared.model -/** Definition of the possible scenes for the communal UI. */ -sealed class CommunalSceneKey( - private val loggingName: String, -) { - /** The communal scene containing the hub UI. */ - object Communal : CommunalSceneKey("communal") +import com.android.compose.animation.scene.SceneKey +/** Definition of the possible scenes for the communal UI. */ +object CommunalScenes { /** The default scene, shows nothing and is only there to allow swiping to communal. */ - object Blank : CommunalSceneKey("blank") + @JvmField val Blank = SceneKey("blank") - override fun toString(): String { - return loggingName - } + /** The communal scene containing the hub UI. */ + @JvmField val Communal = SceneKey("communal") - companion object { - val DEFAULT = Blank - } + @JvmField val Default = Blank } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt deleted file mode 100644 index d834715768c9..000000000000 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2023 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.communal.shared.model - -import kotlinx.coroutines.flow.Flow - -/** - * This is a fork of the `com.android.compose.animation.scene.ObservableTransitionState` class. - * - * TODO(b/315490861): remove this fork, once we can compile Compose into System UI. - */ -sealed class ObservableCommunalTransitionState { - /** No transition/animation is currently running. */ - data class Idle(val scene: CommunalSceneKey) : ObservableCommunalTransitionState() - - /** There is a transition animating between two scenes. */ - data class Transition( - val fromScene: CommunalSceneKey, - val toScene: CommunalSceneKey, - val progress: Flow<Float>, - - /** - * Whether the transition was originally triggered by user input rather than being - * programmatic. If this value is initially true, it will remain true until the transition - * fully completes, even if the user input that triggered the transition has ended. Any - * sub-transitions launched by this one will inherit this value. For example, if the user - * drags a pointer but does not exceed the threshold required to transition to another - * scene, this value will remain true after the pointer is no longer touching the screen and - * will be true in any transition created to animate back to the original position. - */ - val isInitiatedByUserInput: Boolean, - - /** - * Whether user input is currently driving the transition. For example, if a user is - * dragging a pointer, this emits true. Once they lift their finger, this emits false while - * the transition completes/settles. - */ - val isUserInputOngoing: Flow<Boolean>, - ) : ObservableCommunalTransitionState() -} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 3ec9a268f80c..35372cd28c15 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -18,10 +18,10 @@ package com.android.systemui.communal.ui.viewmodel import android.content.ComponentName import android.os.UserHandle +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.media.controls.ui.view.MediaHost import kotlinx.coroutines.flow.Flow @@ -34,7 +34,7 @@ abstract class BaseCommunalViewModel( private val communalInteractor: CommunalInteractor, val mediaHost: MediaHost, ) { - val currentScene: Flow<CommunalSceneKey> = communalInteractor.desiredScene + val currentScene: Flow<SceneKey> = communalInteractor.desiredScene /** Whether widgets are currently being re-ordered. */ open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false) @@ -45,7 +45,7 @@ abstract class BaseCommunalViewModel( val selectedKey: StateFlow<String?> get() = _selectedKey - fun onSceneChanged(scene: CommunalSceneKey) { + fun onSceneChanged(scene: SceneKey) { communalInteractor.onSceneChanged(scene) } @@ -54,7 +54,7 @@ abstract class BaseCommunalViewModel( * * Note that you must call is with `null` when the UI is done or risk a memory leak. */ - fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) { + fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { communalInteractor.setTransitionState(transitionState) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index fc9a7df4744d..35b27aaeb6bc 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -21,6 +21,7 @@ import com.android.systemui.communal.domain.interactor.CommunalTutorialInteracto import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger import com.android.systemui.log.dagger.CommunalLog @@ -46,6 +47,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch /** The default view model used for showing the communal hub. */ +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class CommunalViewModel @Inject @@ -54,6 +56,7 @@ constructor( private val communalInteractor: CommunalInteractor, tutorialInteractor: CommunalTutorialInteractor, shadeInteractor: ShadeInteractor, + deviceEntryInteractor: DeviceEntryInteractor, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, @CommunalLog logBuffer: LogBuffer, ) : BaseCommunalViewModel(communalInteractor, mediaHost) { @@ -87,6 +90,8 @@ constructor( /** Whether touches should be disabled in communal */ val touchesAllowed: Flow<Boolean> = not(shadeInteractor.isAnyFullyExpanded) + val deviceUnlocked: Flow<Boolean> = deviceEntryInteractor.isUnlocked + init { // Initialize our media host for the UMO. This only needs to happen once and must be done // before the MediaHierarchyManager attempts to move the UMO to the hub. diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 48b3e4c3616d..b6ad26b24dc7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -36,7 +36,7 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.compose.theme.PlatformTheme import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.shared.log.CommunalUiEvent -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.compose.CommunalHub import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent @@ -173,7 +173,7 @@ constructor( private fun onEditDone() { try { - communalViewModel.onSceneChanged(CommunalSceneKey.Communal) + communalViewModel.onSceneChanged(CommunalScenes.Communal) checkNotNull(windowManagerService).lockNow(/* options */ null) finish() } catch (e: RemoteException) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index f7bc5cdc69c2..a4011fd7718c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -43,6 +43,7 @@ import com.android.systemui.reardisplay.RearDisplayModule; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.rotationlock.RotationLockModule; +import com.android.systemui.rotationlock.RotationLockNewModule; import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.MultiUserUtilsModule; @@ -110,6 +111,7 @@ import javax.inject.Named; RearDisplayModule.class, ReferenceScreenshotModule.class, RotationLockModule.class, + RotationLockNewModule.class, ScreenDecorationsModule.class, SystemActionsModule.class, ShadeModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 21fd87c1c241..029a4f33cd27 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -25,7 +25,7 @@ import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -81,9 +81,9 @@ constructor( val isDeviceEntered: StateFlow<Boolean> = sceneInteractor.currentScene .filter { currentScene -> - currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen + currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen } - .map { it == SceneKey.Gone } + .map { it == Scenes.Gone } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -148,12 +148,12 @@ constructor( applicationScope.launch { if (isAuthenticationRequired()) { sceneInteractor.changeScene( - toScene = SceneKey.Bouncer, + toScene = Scenes.Bouncer, loggingReason = "request to unlock device while authentication required", ) } else { sceneInteractor.changeScene( - toScene = SceneKey.Gone, + toScene = Scenes.Gone, loggingReason = "request to unlock device while authentication isn't required", ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt index f9084e5a6191..89433d3e8abb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt @@ -20,6 +20,7 @@ import android.app.Dialog import android.content.Context import android.view.Gravity import android.view.Window +import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE @@ -45,7 +46,7 @@ constructor( } private fun createStickyKeyIndicator(viewModel: StickyKeysIndicatorViewModel): Dialog { - return ComponentDialog(context, R.style.Theme_SystemUI_Dialog).apply { + return ComponentDialog(context, R.style.Theme_SystemUI_Dialog_StickyKeys).apply { // because we're requesting window feature it must be called before setting content window?.setStickyKeyWindowAttributes() setContentView(createStickyKeyIndicatorView(context, viewModel)) @@ -61,6 +62,9 @@ constructor( attributes = WindowManager.LayoutParams().apply { copyFrom(attributes) + // needed because we're above system bars windows, see [TYPE_STATUS_BAR_SUB_PANEL] + receiveInsetsIgnoringZOrder = true + fitInsetsTypes = WindowInsets.Type.systemBars() title = "StickyKeysIndicator" } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index ad0c3fb0c53a..64e28700aa74 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -24,6 +24,7 @@ import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.data.repository.FacePropertyRepository import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.common.shared.model.Position import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main @@ -79,6 +80,12 @@ interface KeyguardRepository { val keyguardAlpha: StateFlow<Float> /** + * Observable of the relative offset of the lock-screen clock from its natural position on the + * screen. + */ + val clockPosition: StateFlow<Position> + + /** * Observable for whether the keyguard is showing. * * Note: this is also `true` when the lock-screen is occluded with an `Activity` "above" it in @@ -233,6 +240,11 @@ interface KeyguardRepository { fun setKeyguardAlpha(alpha: Float) /** + * Sets the relative offset of the lock-screen clock from its natural position on the screen. + */ + fun setClockPosition(x: Int, y: Int) + + /** * Returns whether the keyguard bottom area should be constrained to the top of the lock icon */ fun isUdfpsSupported(): Boolean @@ -311,6 +323,9 @@ constructor( private val _keyguardAlpha = MutableStateFlow(1f) override val keyguardAlpha = _keyguardAlpha.asStateFlow() + private val _clockPosition = MutableStateFlow(Position(0, 0)) + override val clockPosition = _clockPosition.asStateFlow() + private val _clockShouldBeCentered = MutableStateFlow(true) override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered.asStateFlow() @@ -662,6 +677,10 @@ constructor( _keyguardAlpha.value = alpha } + override fun setClockPosition(x: Int, y: Int) { + _clockPosition.value = Position(x, y) + } + override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported override fun setQuickSettingsVisible(isVisible: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt index ee892a800b63..7ae70a9a3e7c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt @@ -63,19 +63,18 @@ constructor( burnInHelperWrapper.burnInProgressOffset() ) - /** Given the max x,y dimens, determine the current translation shifts. */ - fun burnIn(xDimenResourceId: Int, yDimenResourceId: Int): Flow<BurnInModel> { - return combine( - burnInOffset(xDimenResourceId, isXAxis = true), - burnInOffset(yDimenResourceId, isXAxis = false).map { - it * 2 - context.resources.getDimensionPixelSize(yDimenResourceId) + val keyguardBurnIn: Flow<BurnInModel> = + combine( + burnInOffset(R.dimen.burn_in_prevention_offset_x, isXAxis = true), + burnInOffset(R.dimen.burn_in_prevention_offset_y, isXAxis = false).map { + it * 2 - + context.resources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y) } ) { translationX, translationY -> BurnInModel(translationX, translationY, burnInHelperWrapper.burnInScale()) } .distinctUntilChanged() .stateIn(scope, SharingStarted.Lazily, BurnInModel()) - } /** * Use for max burn-in offsets that are NOT specified in pixels. This flow will recalculate the diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 617982f8532c..dbd5e26eacfc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -93,14 +93,22 @@ constructor( startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, keyguardInteractor.biometricUnlockState, + keyguardInteractor.primaryBouncerShowing, ) - .collect { (_, isKeyguardShowing, lastStartedStep, occluded, biometricUnlockState) - -> + .collect { + ( + _, + isKeyguardShowing, + lastStartedStep, + occluded, + biometricUnlockState, + primaryBouncerShowing) -> if ( lastStartedStep.to == KeyguardState.AOD && !occluded && !isWakeAndUnlock(biometricUnlockState) && - isKeyguardShowing + isKeyguardShowing && + !primaryBouncerShowing ) { val modeOnCanceled = if (lastStartedStep.from == KeyguardState.LOCKSCREEN) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index baa865d22f70..8591fe782d3a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -31,6 +31,8 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch @@ -59,6 +61,14 @@ constructor( listenForTransitionToCamera(scope, keyguardInteractor) } + private val canDismissLockScreen: Flow<Boolean> = + combine( + keyguardInteractor.isKeyguardShowing, + keyguardInteractor.isKeyguardDismissible, + ) { isKeyguardShowing, isKeyguardDismissible -> + isKeyguardDismissible && !isKeyguardShowing + } + private fun listenForDozingToAny() { scope.launch { powerInteractor.isAwake @@ -68,8 +78,8 @@ constructor( startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, communalInteractor.isIdleOnCommunal, - keyguardInteractor.isKeyguardShowing, - keyguardInteractor.isKeyguardDismissible, + canDismissLockScreen, + keyguardInteractor.primaryBouncerShowing, ) .collect { ( @@ -78,16 +88,18 @@ constructor( lastStartedTransition, occluded, isIdleOnCommunal, - isKeyguardShowing, - isKeyguardDismissible) -> + canDismissLockScreen, + primaryBouncerShowing) -> if (!(isAwake && lastStartedTransition.to == KeyguardState.DOZING)) { return@collect } startTransitionTo( if (isWakeAndUnlock(biometricUnlockState)) { KeyguardState.GONE - } else if (isKeyguardDismissible && !isKeyguardShowing) { + } else if (canDismissLockScreen) { KeyguardState.GONE + } else if (primaryBouncerShowing) { + KeyguardState.PRIMARY_BOUNCER } else if (occluded) { KeyguardState.OCCLUDED } else if (isIdleOnCommunal) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt index 6cb1eb493db3..744301019dfc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt @@ -20,7 +20,7 @@ import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTransitionProgress -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -55,9 +55,9 @@ constructor( ) { val toScene = if (fromState == KeyguardState.GLANCEABLE_HUB) { - CommunalSceneKey.Blank + CommunalScenes.Blank } else { - CommunalSceneKey.Communal + CommunalScenes.Communal } var transitionId: UUID? = null diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt index e5bb5a081b70..d2a7486eed0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt @@ -22,8 +22,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardRepository import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow /** Encapsulates business-logic specifically related to the keyguard bottom area. */ @SysUISingleton @@ -37,12 +35,10 @@ constructor( /** The amount of alpha for the UI components of the bottom area. */ val alpha: Flow<Float> = repository.bottomAreaAlpha /** The position of the keyguard clock. */ - private val _clockPosition = MutableStateFlow(Position(0, 0)) - @Deprecated("with migrateClocksToBlueprint()") - val clockPosition: Flow<Position> = _clockPosition.asStateFlow() + val clockPosition: Flow<Position> = repository.clockPosition fun setClockPosition(x: Int, y: Int) { - _clockPosition.value = Position(x, y) + repository.setClockPosition(x, y) } fun setAlpha(alpha: Float) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 8b06b85e70c9..5410b10a4b93 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -27,6 +27,7 @@ import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.shared.model.NotificationContainerBounds +import com.android.systemui.common.shared.model.Position import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -42,7 +43,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.kotlin.sample @@ -231,6 +232,9 @@ constructor( /** The approximate location on the screen of the face unlock sensor, if one is available. */ val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation + /** The position of the keyguard clock. */ + val clockPosition: Flow<Position> = repository.clockPosition + @Deprecated("Use the relevant TransitionViewModel") val keyguardAlpha: Flow<Float> = repository.keyguardAlpha @@ -288,7 +292,7 @@ constructor( sceneInteractorProvider .get() .transitioningTo - .map { it == SceneKey.Lockscreen } + .map { it == Scenes.Lockscreen } .distinctUntilChanged() .flatMapLatest { isTransitioningToLockscreenScene -> if (isTransitioningToLockscreenScene) { @@ -338,6 +342,10 @@ constructor( repository.setQuickSettingsVisible(isVisible) } + fun setClockPosition(x: Int, y: Int) { + repository.setClockPosition(x, y) + } + fun setAlpha(alpha: Float) { repository.setKeyguardAlpha(alpha) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 462d5e6568b0..4812e03ec3f6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -42,7 +42,7 @@ import kotlin.math.max import kotlinx.coroutines.launch private const val TAG = "KeyguardBlueprintViewBinder" -private const val DEBUG = true +private const val DEBUG = false @SysUISingleton class KeyguardBlueprintViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt index e67a3245bc85..98bebd091f1a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt @@ -21,8 +21,6 @@ import android.content.Context import android.view.View import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet -import androidx.constraintlayout.widget.ConstraintSet.BOTTOM -import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.view.KeyguardRootView @@ -39,18 +37,13 @@ constructor( private val clockViewModel: KeyguardClockViewModel, ) : KeyguardSection() { private lateinit var burnInLayer: AodBurnInLayer - private lateinit var emptyView: View override fun addViews(constraintLayout: ConstraintLayout) { if (!migrateClocksToBlueprint()) { return } // The burn-in layer requires at least 1 view at all times - emptyView = - View(context, null).apply { - id = R.id.burn_in_layer_empty_view - visibility = View.GONE - } + val emptyView = View(context, null).apply { id = View.generateViewId() } constraintLayout.addView(emptyView) burnInLayer = AodBurnInLayer(context).apply { @@ -77,13 +70,6 @@ constructor( if (!migrateClocksToBlueprint()) { return } - - constraintSet.apply { - // The empty view should not occupy any space - constrainHeight(R.id.burn_in_layer_empty_view, 1) - constrainWidth(R.id.burn_in_layer_empty_view, 0) - connect(R.id.burn_in_layer_empty_view, BOTTOM, PARENT_ID, BOTTOM) - } } override fun removeViews(constraintLayout: ConstraintLayout) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index 4ddd039edbaa..6184c82cbff7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -199,7 +199,7 @@ class ClockSizeTransition( private val TRANSITION_PROPERTIES = arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS) - private val DEBUG = true + private val DEBUG = false private val TAG = VisibilityBoundsTransition::class.simpleName!! } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt index 62fc1da5a444..7be390a4526f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import android.util.Log import android.util.MathUtils import com.android.app.animation.Interpolators import com.android.keyguard.KeyguardClockSwitch @@ -63,8 +62,6 @@ constructor( private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel, private val keyguardClockViewModel: KeyguardClockViewModel, ) { - private val TAG = "AodBurnInViewModel" - /** Horizontal translation for elements that need to apply anti-burn-in tactics. */ fun translationX( params: BurnInParameters, @@ -74,17 +71,8 @@ constructor( /** Vertical translation for elements that need to apply anti-burn-in tactics. */ fun translationY( - burnInParams: BurnInParameters, + params: BurnInParameters, ): Flow<Float> { - val params = - if (burnInParams.minViewY < burnInParams.topInset) { - // minViewY should never be below the inset. Correct it if needed - Log.w(TAG, "minViewY is below topInset: $burnInParams") - burnInParams.copy(minViewY = burnInParams.topInset) - } else { - burnInParams - } - return configurationInteractor .dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y) .flatMapLatest { enterFromTopAmount -> @@ -137,10 +125,7 @@ constructor( keyguardTransitionInteractor.dozeAmountTransition.map { Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value) }, - burnInInteractor.burnIn( - xDimenResourceId = R.dimen.burn_in_prevention_offset_x, - yDimenResourceId = R.dimen.burn_in_prevention_offset_y - ) + burnInInteractor.keyguardBurnIn, ) { interpolated, burnIn -> val useScaleOnly = (clockController(params.clockControllerProvider) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index 31e809356e02..bd19c8006e23 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -180,7 +180,7 @@ constructor( } private val isUnlocked: Flow<Boolean> = - deviceEntryInteractor.isUnlocked.flatMapLatest { isUnlocked -> + keyguardInteractor.isKeyguardDismissible.flatMapLatest { isUnlocked -> if (!isUnlocked) { flowOf(false) } else { @@ -197,7 +197,7 @@ constructor( val iconType: Flow<DeviceEntryIconView.IconType> = combine( deviceEntryUdfpsInteractor.isListeningForUdfps, - keyguardInteractor.isKeyguardDismissible, + isUnlocked, ) { isListeningForUdfps, isUnlocked -> if (isListeningForUdfps) { DeviceEntryIconView.IconType.FINGERPRINT diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt index e35e06533f8c..6458edaecd9e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt @@ -17,14 +17,10 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.Flags.keyguardBottomAreaRefactor -import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.doze.util.BurnInHelperWrapper -import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.keyguard.shared.model.BurnInModel -import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -36,10 +32,9 @@ class KeyguardIndicationAreaViewModel @Inject constructor( private val keyguardInteractor: KeyguardInteractor, - private val bottomAreaInteractor: KeyguardBottomAreaInteractor, + bottomAreaInteractor: KeyguardBottomAreaInteractor, keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel, private val burnInHelperWrapper: BurnInHelperWrapper, - private val burnInInteractor: BurnInInteractor, private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel, configurationInteractor: ConfigurationInteractor, ) { @@ -68,37 +63,24 @@ constructor( } .distinctUntilChanged() } - - private val burnIn: Flow<BurnInModel> = - burnInInteractor - .burnIn( - xDimenResourceId = R.dimen.burn_in_prevention_offset_x, - yDimenResourceId = R.dimen.default_burn_in_prevention_offset, - ) - .distinctUntilChanged() - /** An observable for the x-offset by which the indication area should be translated. */ val indicationAreaTranslationX: Flow<Float> = - if (migrateClocksToBlueprint() || keyguardBottomAreaRefactor()) { - burnIn.map { it.translationX.toFloat() } + if (keyguardBottomAreaRefactor()) { + keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged() } else { bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged() } /** Returns an observable for the y-offset by which the indication area should be translated. */ fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> { - return if (migrateClocksToBlueprint()) { - burnIn.map { it.translationY.toFloat() } - } else { - keyguardInteractor.dozeAmount - .map { dozeAmount -> - dozeAmount * - (burnInHelperWrapper.burnInOffset( - /* amplitude = */ defaultBurnInOffset * 2, - /* xAxis= */ false, - ) - defaultBurnInOffset) - } - .distinctUntilChanged() - } + return keyguardInteractor.dozeAmount + .map { dozeAmount -> + dozeAmount * + (burnInHelperWrapper.burnInOffset( + /* amplitude = */ defaultBurnInOffset * 2, + /* xAxis= */ false, + ) - defaultBurnInOffset) + } + .distinctUntilChanged() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 38d5e0f74b28..1760b927a6cd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -75,6 +75,7 @@ constructor( private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel, private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, + private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, private val glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel, @@ -171,6 +172,7 @@ constructor( aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState), dozingToGoneTransitionViewModel.lockscreenAlpha(viewState), dozingToLockscreenTransitionViewModel.lockscreenAlpha, + dreamingToLockscreenTransitionViewModel.lockscreenAlpha, glanceableHubToLockscreenTransitionViewModel.keyguardAlpha, goneToAodTransitionViewModel.enterFromTopAnimationAlpha, goneToDozingTransitionViewModel.lockscreenAlpha, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 9afe8fcd93d0..b60e99973348 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -16,11 +16,12 @@ package com.android.systemui.keyguard.ui.viewmodel +import com.android.compose.animation.scene.SceneKey import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -51,13 +52,13 @@ constructor( ) private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey { - return if (isUnlocked) SceneKey.Gone else SceneKey.Bouncer + return if (isUnlocked) Scenes.Gone else Scenes.Bouncer } /** The key of the scene we should switch to when swiping left. */ val leftDestinationSceneKey: StateFlow<SceneKey?> = communalInteractor.isCommunalAvailable - .map { available -> if (available) SceneKey.Communal else null } + .map { available -> if (available) Scenes.Communal else null } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt index 6eb62263eb9a..e7b6e6373f1c 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt @@ -16,11 +16,12 @@ package com.android.systemui.model +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE @@ -67,11 +68,11 @@ constructor( */ val EvaluatorByFlag = mapOf<Int, (SceneKey) -> Boolean>( - SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != SceneKey.Gone }, - SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == SceneKey.Shade }, - SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == SceneKey.QuickSettings }, - SYSUI_STATE_BOUNCER_SHOWING to { it == SceneKey.Bouncer }, - SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == SceneKey.Lockscreen }, + SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != Scenes.Gone }, + SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == Scenes.Shade }, + SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == Scenes.QuickSettings }, + SYSUI_STATE_BOUNCER_SHOWING to { it == Scenes.Bouncer }, + SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == Scenes.Lockscreen }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt index c74a71c52260..5c4915689f22 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt @@ -25,11 +25,11 @@ import com.android.systemui.dagger.qualifiers.DisplayId * ``` * sysuiState.updateFlags( * displayId, - * SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != SceneKey.Gone), - * SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == SceneKey.Shade), - * SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == SceneKey.QuickSettings), - * SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == SceneKey.Bouncer), - * SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == SceneKey.Lockscreen), + * SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != Scenes.Gone), + * SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == Scenes.Shade), + * SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == Scenes.QuickSettings), + * SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == Scenes.Bouncer), + * SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == Scenes.Lockscreen), * ) * ``` * diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt new file mode 100644 index 000000000000..736e1a5cb9b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation.domain.interactor + +import android.Manifest +import android.content.pm.PackageManager +import android.content.res.Resources +import android.os.UserHandle +import com.android.systemui.camera.data.repository.CameraAutoRotateRepository +import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepository +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.statusbar.policy.RotationLockController +import com.android.systemui.util.kotlin.isBatteryPowerSaveEnabled +import com.android.systemui.util.kotlin.isRotationLockEnabled +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOf + +/** Observes rotation lock state changes providing the [RotationLockTileModel]. */ +class RotationLockTileDataInteractor +@Inject +constructor( + private val rotationLockController: RotationLockController, + private val batteryController: BatteryController, + private val cameraAutoRotateRepository: CameraAutoRotateRepository, + private val cameraSensorPrivacyRepository: CameraSensorPrivacyRepository, + private val packageManager: PackageManager, + @Main private val resources: Resources, +) : QSTileDataInteractor<RotationLockTileModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<RotationLockTileModel> = + combine( + rotationLockController.isRotationLockEnabled(), + cameraSensorPrivacyRepository.isEnabled(user), + batteryController.isBatteryPowerSaveEnabled(), + cameraAutoRotateRepository.isCameraAutoRotateSettingEnabled(user) + ) { + isRotationLockEnabled, + isCamPrivacySensorEnabled, + isBatteryPowerSaveEnabled, + isCameraAutoRotateEnabled, + -> + RotationLockTileModel( + isRotationLockEnabled, + isCameraRotationEnabled( + isBatteryPowerSaveEnabled, + isCamPrivacySensorEnabled, + isCameraAutoRotateEnabled + ), + ) + } + + override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true) + + private fun hasSufficientPermission(): Boolean { + val rotationPackage: String = packageManager.rotationResolverPackageName + return rotationPackage != null && + packageManager.checkPermission(Manifest.permission.CAMERA, rotationPackage) == + PackageManager.PERMISSION_GRANTED + } + + private fun isCameraRotationEnabled( + isBatteryPowerSaverModeOn: Boolean, + isCameraSensorPrivacyEnabled: Boolean, + isCameraAutoRotateEnabled: Boolean + ): Boolean = + resources.getBoolean(com.android.internal.R.bool.config_allowRotationResolver) && + !isBatteryPowerSaverModeOn && + !isCameraSensorPrivacyEnabled && + hasSufficientPermission() && + isCameraAutoRotateEnabled +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt new file mode 100644 index 000000000000..8530926e68e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.statusbar.policy.RotationLockController +import javax.inject.Inject + +/** Handles rotation lock tile clicks. */ +class RotationLockTileUserActionInteractor +@Inject +constructor( + private val controller: RotationLockController, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<RotationLockTileModel> { + + override suspend fun handleInput(input: QSTileInput<RotationLockTileModel>) { + with(input) { + when (action) { + is QSTileUserAction.Click -> { + controller.setRotationLocked(!data.isRotationLocked, CALLER) + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.view, + Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS) + ) + } + } + } + } + + companion object { + private const val CALLER = "QSTileUserActionInteractor#handleInput" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKey.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/model/RotationLockTileModel.kt index 87332ae8e084..32e6cb8cb52c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKey.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/model/RotationLockTileModel.kt @@ -14,13 +14,10 @@ * limitations under the License. */ -package com.android.systemui.scene.shared.model +package com.android.systemui.qs.tiles.impl.rotation.domain.model -/** - * Key for a transition. This can be used to specify which transition spec should be used when - * starting the transition between two scenes. - */ -data class TransitionKey( - val debugName: String, - val identity: Any = Object(), +/** Model for rotation lock tile */ +class RotationLockTileModel( + val isRotationLocked: Boolean, + val isCameraRotationEnabled: Boolean, ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt new file mode 100644 index 000000000000..070cdef336e5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation.ui.mapper + +import android.content.res.Resources +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import com.android.systemui.statusbar.policy.DevicePostureController +import javax.inject.Inject + +/** Maps [RotationLockTileModel] to [QSTileState]. */ +class RotationLockTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Resources.Theme, + private val devicePostureController: DevicePostureController +) : QSTileDataToStateMapper<RotationLockTileModel> { + override fun map(config: QSTileConfig, data: RotationLockTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + this.label = resources.getString(R.string.quick_settings_rotation_unlocked_label) + this.contentDescription = + resources.getString(R.string.accessibility_quick_settings_rotation) + + if (data.isRotationLocked) { + activationState = QSTileState.ActivationState.INACTIVE + this.secondaryLabel = EMPTY_SECONDARY_STRING + this.icon = { + Icon.Loaded( + resources.getDrawable(R.drawable.qs_auto_rotate_icon_off, theme), + contentDescription = null + ) + } + } else { + activationState = QSTileState.ActivationState.ACTIVE + this.secondaryLabel = + if (data.isCameraRotationEnabled) { + resources.getString(R.string.rotation_lock_camera_rotation_on) + } else { + EMPTY_SECONDARY_STRING + } + this.icon = { + Icon.Loaded( + resources.getDrawable(R.drawable.qs_auto_rotate_icon_on, theme), + contentDescription = null + ) + } + } + if (isDeviceFoldable()) { + this.secondaryLabel = getSecondaryLabelWithPosture(this.activationState) + } + this.stateDescription = this.secondaryLabel + this.sideViewIcon = QSTileState.SideViewIcon.None + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } + + private fun isDeviceFoldable(): Boolean { + val intArray = resources.getIntArray(com.android.internal.R.array.config_foldedDeviceStates) + return intArray.isNotEmpty() + } + + private fun getSecondaryLabelWithPosture(activationState: QSTileState.ActivationState): String { + val stateNames = resources.getStringArray(R.array.tile_states_rotation) + val stateName = + stateNames[ + if (activationState == QSTileState.ActivationState.ACTIVE) ON_INDEX else OFF_INDEX] + val posture = + if ( + devicePostureController.devicePosture == + DevicePostureController.DEVICE_POSTURE_CLOSED + ) + resources.getString(R.string.quick_settings_rotation_posture_folded) + else resources.getString(R.string.quick_settings_rotation_posture_unfolded) + + return resources.getString( + R.string.rotation_tile_with_posture_secondary_label_template, + stateName, + posture + ) + } + + private companion object { + const val EMPTY_SECONDARY_STRING = "" + const val OFF_INDEX = 1 + const val ON_INDEX = 2 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 17454a97f5d2..34f66b85def1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -17,14 +17,16 @@ package com.android.systemui.qs.ui.viewmodel import androidx.lifecycle.LifecycleOwner +import com.android.compose.animation.scene.Back +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.QSSceneAdapter -import com.android.systemui.scene.shared.model.Direction -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.UserAction -import com.android.systemui.scene.shared.model.UserActionResult +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import java.util.concurrent.atomic.AtomicBoolean @@ -45,13 +47,11 @@ constructor( val destinationScenes = qsSceneAdapter.isCustomizing.map { customizing -> if (customizing) { - mapOf<UserAction, UserActionResult>( - UserAction.Back to UserActionResult(SceneKey.QuickSettings) - ) + mapOf<UserAction, UserActionResult>(Back to UserActionResult(Scenes.QuickSettings)) } else { mapOf( - UserAction.Back to UserActionResult(SceneKey.Shade), - UserAction.Swipe(Direction.UP) to UserActionResult(SceneKey.Shade), + Back to UserActionResult(Scenes.Shade), + Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt new file mode 100644 index 000000000000..eb64dd609a72 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 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.rotationlock + +import com.android.systemui.camera.CameraRotationModule +import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory +import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor +import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel +import com.android.systemui.qs.tiles.impl.rotation.ui.mapper.RotationLockTileMapper +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.res.R +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey + +@Module(includes = [CameraRotationModule::class]) +interface RotationLockNewModule { + companion object { + private const val ROTATION_TILE_SPEC = "rotation" + + /** Inject rotation tile config */ + @Provides + @IntoMap + @StringKey(ROTATION_TILE_SPEC) + fun provideRotationTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(ROTATION_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.qs_auto_rotate_icon_off, + labelRes = R.string.quick_settings_rotation_unlocked_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + ) + + /** Inject Rotation tile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(ROTATION_TILE_SPEC) + fun provideRotationTileViewModel( + factory: QSTileViewModelFactory.Static<RotationLockTileModel>, + mapper: RotationLockTileMapper, + stateInteractor: RotationLockTileDataInteractor, + userActionInteractor: RotationLockTileUserActionInteractor + ): QSTileViewModel = + factory.create( + TileSpec.create(ROTATION_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt index 356eb858d78f..afd0746f4696 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt @@ -18,7 +18,7 @@ package com.android.systemui.scene import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule import com.android.systemui.scene.shared.model.SceneContainerConfig -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import dagger.Module import dagger.Provides @@ -44,11 +44,11 @@ object KeyguardlessSceneContainerFrameworkModule { // last one is top-most. sceneKeys = listOf( - SceneKey.Gone, - SceneKey.QuickSettings, - SceneKey.Shade, + Scenes.Gone, + Scenes.QuickSettings, + Scenes.Shade, ), - initialSceneKey = SceneKey.Gone, + initialSceneKey = Scenes.Gone, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index 7d2468b2f016..62b0914fab79 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -22,7 +22,7 @@ import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInte import com.android.systemui.scene.domain.startable.SceneContainerStartable import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule import com.android.systemui.scene.shared.model.SceneContainerConfig -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import dagger.Binds import dagger.Module import dagger.Provides @@ -67,14 +67,14 @@ interface SceneContainerFrameworkModule { // last one is top-most. sceneKeys = listOf( - SceneKey.Gone, - SceneKey.Communal, - SceneKey.Lockscreen, - SceneKey.Bouncer, - SceneKey.QuickSettings, - SceneKey.Shade, + Scenes.Gone, + Scenes.Communal, + Scenes.Lockscreen, + Scenes.Bouncer, + Scenes.QuickSettings, + Scenes.Shade, ), - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt index c10e51b68ba2..0665c9e1b802 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt @@ -18,7 +18,7 @@ package com.android.systemui.scene import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule import com.android.systemui.scene.shared.model.SceneContainerConfig -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import dagger.Module import dagger.Provides @@ -44,11 +44,11 @@ object ShadelessSceneContainerFrameworkModule { // last one is top-most. sceneKeys = listOf( - SceneKey.Gone, - SceneKey.Lockscreen, - SceneKey.Bouncer, + Scenes.Gone, + Scenes.Lockscreen, + Scenes.Bouncer, ), - initialSceneKey = SceneKey.Lockscreen, + initialSceneKey = Scenes.Lockscreen, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt index e60dff183148..994b01216c22 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt @@ -18,12 +18,12 @@ package com.android.systemui.scene.data.repository +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSource -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.TransitionKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt index 36350f8af455..3a5ea5c6a064 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt @@ -18,10 +18,11 @@ package com.android.systemui.scene.domain.interactor +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -53,7 +54,7 @@ constructor( when (state) { is ObservableTransitionState.Idle -> flowOf( - if (state.scene != SceneKey.Gone) { + if (state.scene != Scenes.Gone) { // When resting on a non-Gone scene, the panel is fully expanded. 1f } else { @@ -64,7 +65,7 @@ constructor( ) is ObservableTransitionState.Transition -> when { - state.fromScene == SceneKey.Gone -> + state.fromScene == Scenes.Gone -> if (state.toScene.isExpandable()) { // Moving from Gone to a scene that can animate-expand has a // panel @@ -77,7 +78,7 @@ constructor( // the panel fully expanded. flowOf(1f) } - state.toScene == SceneKey.Gone -> + state.toScene == Scenes.Gone -> if (state.fromScene.isExpandable()) { // Moving to Gone from a scene that can animate-expand has a // panel @@ -99,6 +100,6 @@ constructor( } private fun SceneKey.isExpandable(): Boolean { - return this == SceneKey.Shade || this == SceneKey.QuickSettings + return this == Scenes.Shade || this == Scenes.QuickSettings } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 6b7c672fbfe0..75bf131afdf9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -16,14 +16,15 @@ package com.android.systemui.scene.domain.interactor +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.logger.SceneLogger -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.TransitionKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwiseBy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -155,12 +156,13 @@ constructor( * desired scene. Once enough of the transition has occurred, the [currentScene] will become * [toScene] (unless the transition is canceled by user action or another call to this method). */ + @JvmOverloads fun changeScene( toScene: SceneKey, loggingReason: String, transitionKey: TransitionKey? = null, ) { - check(toScene != SceneKey.Gone || deviceUnlockedInteractor.isDeviceUnlocked.value) { + check(toScene != Scenes.Gone || deviceUnlockedInteractor.isDeviceUnlocked.value) { "Cannot change to the Gone scene while the device is locked. Logging reason for scene" + " change was: $loggingReason" } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt index 1c37908235bd..c736707ecd2d 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.domain.interactor +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -24,8 +25,7 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository import com.android.systemui.scene.shared.flag.SceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.init.NotificationsController @@ -77,12 +77,12 @@ constructor( .map { state -> when (state) { is ObservableTransitionState.Idle -> - state.scene == SceneKey.Shade || state.scene == SceneKey.Lockscreen + state.scene == Scenes.Shade || state.scene == Scenes.Lockscreen is ObservableTransitionState.Transition -> - state.toScene == SceneKey.Shade || - state.toScene == SceneKey.Lockscreen || - state.fromScene == SceneKey.Shade || - state.fromScene == SceneKey.Lockscreen + state.toScene == Scenes.Shade || + state.toScene == Scenes.Lockscreen || + state.fromScene == Scenes.Shade || + state.fromScene == Scenes.Lockscreen } } .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 034f87f4c72f..6df57edd34c3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -19,6 +19,8 @@ package com.android.systemui.scene.domain.startable import android.app.StatusBarManager +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.CoreStartable import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -41,8 +43,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.logger.SceneLogger -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled @@ -140,14 +141,14 @@ constructor( .mapNotNull { state -> when (state) { is ObservableTransitionState.Idle -> { - if (state.scene != SceneKey.Gone) { + if (state.scene != Scenes.Gone) { true to "scene is not Gone" } else { false to "scene is Gone" } } is ObservableTransitionState.Transition -> { - if (state.fromScene == SceneKey.Gone) { + if (state.fromScene == Scenes.Gone) { true to "scene transitioning away from Gone" } else { null @@ -180,9 +181,9 @@ constructor( applicationScope.launch { // TODO (b/308001302): Move this to a bouncer specific interactor. bouncerInteractor.onImeHiddenByUser.collectLatest { - if (sceneInteractor.currentScene.value == SceneKey.Bouncer) { + if (sceneInteractor.currentScene.value == Scenes.Bouncer) { sceneInteractor.changeScene( - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, loggingReason = "IME hidden", ) } @@ -196,13 +197,13 @@ constructor( when { isAnySimLocked -> { switchToScene( - targetSceneKey = SceneKey.Bouncer, + targetSceneKey = Scenes.Bouncer, loggingReason = "Need to authenticate locked SIM card." ) } isUnlocked && canSwipeToEnter == false -> { switchToScene( - targetSceneKey = SceneKey.Gone, + targetSceneKey = Scenes.Gone, loggingReason = "All SIM cards unlocked and device already" + " unlocked and lockscreen doesn't require a swipe to dismiss." @@ -210,7 +211,7 @@ constructor( } else -> { switchToScene( - targetSceneKey = SceneKey.Lockscreen, + targetSceneKey = Scenes.Lockscreen, loggingReason = "All SIM cards unlocked and device still locked" + " or lockscreen still requires a swipe to dismiss." @@ -231,8 +232,8 @@ constructor( transitionState.toScene, ) } - val isOnLockscreen = renderedScenes.contains(SceneKey.Lockscreen) - val isOnBouncer = renderedScenes.contains(SceneKey.Bouncer) + val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen) + val isOnBouncer = renderedScenes.contains(Scenes.Bouncer) if (!isUnlocked) { return@mapNotNull if (isOnLockscreen || isOnBouncer) { // Already on lockscreen or bouncer, no need to change scenes. @@ -240,7 +241,7 @@ constructor( } else { // The device locked while on a scene that's not Lockscreen or Bouncer, // go to Lockscreen. - SceneKey.Lockscreen to + Scenes.Lockscreen to "device locked in non-Lockscreen and non-Bouncer scene" } } @@ -250,7 +251,7 @@ constructor( when { isOnBouncer -> // When the device becomes unlocked in Bouncer, go to Gone. - SceneKey.Gone to "device was unlocked in Bouncer scene" + Scenes.Gone to "device was unlocked in Bouncer scene" isOnLockscreen -> // The lockscreen should be dismissed automatically in 2 scenarios: // 1. When face auth bypass is enabled and authentication happens while @@ -262,11 +263,11 @@ constructor( // authentication attempt. when { isBypassEnabled -> - SceneKey.Gone to + Scenes.Gone to "device has been unlocked on lockscreen with bypass" + " enabled" canSwipeToEnter == false -> - SceneKey.Gone to + Scenes.Gone to "device has been unlocked on lockscreen using an active" + " authentication mechanism" else -> null @@ -287,7 +288,7 @@ constructor( powerInteractor.isAsleep.collect { isAsleep -> if (isAsleep) { switchToScene( - targetSceneKey = SceneKey.Lockscreen, + targetSceneKey = Scenes.Lockscreen, loggingReason = "device is starting to sleep", ) } else { @@ -295,7 +296,7 @@ constructor( val isUnlocked = deviceEntryInteractor.isUnlocked.value if (isUnlocked && canSwipeToEnter == false) { switchToScene( - targetSceneKey = SceneKey.Gone, + targetSceneKey = Scenes.Gone, loggingReason = "device is waking up while unlocked without the ability" + " to swipe up on lockscreen to enter.", @@ -305,7 +306,7 @@ constructor( AuthenticationMethodModel.Sim ) { switchToScene( - targetSceneKey = SceneKey.Bouncer, + targetSceneKey = Scenes.Bouncer, loggingReason = "device is starting to wake up with a locked sim" ) } @@ -370,7 +371,7 @@ constructor( applicationScope.launch { sceneInteractor.currentScene - .map { it == SceneKey.Bouncer } + .map { it == Scenes.Bouncer } .distinctUntilChanged() .collect { switchedToBouncerScene -> if (switchedToBouncerScene) { @@ -390,7 +391,7 @@ constructor( falsingManager.addFalsingBeliefListener(listener) awaitClose { falsingManager.removeFalsingBeliefListener(listener) } } - .collect { switchToScene(SceneKey.Lockscreen, "Falsing detected.") } + .collect { switchToScene(Scenes.Lockscreen, "Falsing detected.") } } } @@ -403,7 +404,7 @@ constructor( } .distinctUntilChanged() .collect { sceneKey -> - windowController.setNotificationShadeFocusable(sceneKey != SceneKey.Gone) + windowController.setNotificationShadeFocusable(sceneKey != Scenes.Gone) } } } @@ -427,9 +428,9 @@ constructor( // // This is done here in order to match the legacy // implementation. The real reason why is lost to lore and myth. - SceneKey.Lockscreen -> true - SceneKey.Bouncer -> false - SceneKey.Shade -> false + Scenes.Lockscreen -> true + Scenes.Bouncer -> false + Scenes.Shade -> false else -> null } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt index cbf7b3e7a971..f44779ade8db 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt @@ -16,10 +16,10 @@ package com.android.systemui.scene.shared.logger +import com.android.compose.animation.scene.SceneKey import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.log.dagger.SceneFrameworkLog -import com.android.systemui.scene.shared.model.SceneKey import javax.inject.Inject class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer: LogBuffer) { diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt deleted file mode 100644 index f704894e56e2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2023 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.scene.shared.model - -import kotlinx.coroutines.flow.Flow - -/** - * This is a fork of a class by the same name in the `com.android.compose.animation.scene` package. - * - * TODO(b/293899074): remove this fork, once we can compile Compose into System UI. - */ -sealed class ObservableTransitionState { - /** No transition/animation is currently running. */ - data class Idle(val scene: SceneKey) : ObservableTransitionState() - - /** There is a transition animating between two scenes. */ - data class Transition( - val fromScene: SceneKey, - val toScene: SceneKey, - val progress: Flow<Float>, - - /** - * Whether the transition was originally triggered by user input rather than being - * programmatic. If this value is initially true, it will remain true until the transition - * fully completes, even if the user input that triggered the transition has ended. Any - * sub-transitions launched by this one will inherit this value. For example, if the user - * drags a pointer but does not exceed the threshold required to transition to another - * scene, this value will remain true after the pointer is no longer touching the screen and - * will be true in any transition created to animate back to the original position. - */ - val isInitiatedByUserInput: Boolean, - - /** - * Whether user input is currently driving the transition. For example, if a user is - * dragging a pointer, this emits true. Once they lift their finger, this emits false while - * the transition completes/settles. - */ - val isUserInputOngoing: Flow<Boolean>, - ) : ObservableTransitionState() -} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt index 05056c133ca3..939d5bc6588e 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt @@ -16,6 +16,9 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import kotlinx.coroutines.flow.StateFlow /** @@ -53,39 +56,3 @@ interface Scene { */ val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> } - -/** Enumerates all scene framework supported user actions. */ -sealed interface UserAction { - - /** The user is scrolling, dragging, swiping, or flinging. */ - data class Swipe( - /** The direction of the swipe. */ - val direction: Direction, - /** - * The edge from which the swipe originated or `null`, if the swipe didn't start close to an - * edge. - */ - val fromEdge: Edge? = null, - /** The number of pointers that were used (for example, one or two fingers). */ - val pointerCount: Int = 1, - ) : UserAction - - /** The user has hit the back button or performed the back navigation gesture. */ - data object Back : UserAction -} - -/** Enumerates all known "cardinal" directions for user actions. */ -enum class Direction { - LEFT, - UP, - RIGHT, - DOWN, -} - -/** Enumerates all known edges from which a swipe can start. */ -enum class Edge { - LEFT, - TOP, - RIGHT, - BOTTOM, -} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt index 8204edc33fe4..53cdaaab7478 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt @@ -16,6 +16,8 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.SceneKey + /** Models the configuration of the scene container. */ data class SceneContainerConfig( diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt index f7b45e547b7f..0e078d5d8064 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt @@ -16,6 +16,8 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import kotlinx.coroutines.flow.StateFlow /** Defines interface for classes that provide access to scene state. */ diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt index a50830c1f212..69dce83b7136 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt @@ -18,6 +18,8 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt index 609d2b93a36e..73fcca8c6b7f 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt @@ -16,41 +16,37 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.SceneKey + /** * Keys of all known scenes. * * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY. */ -sealed class SceneKey( - private val loggingName: String, -) { +object Scenes { /** * The bouncer is the scene that displays authentication challenges like PIN, password, or * pattern. */ - object Bouncer : SceneKey("bouncer") + @JvmField val Bouncer = SceneKey("bouncer") /** The communal scene shows the glanceable hub when device is locked and docked. */ - object Communal : SceneKey("communal") + @JvmField val Communal = SceneKey("communal") /** * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any * content from the scene framework. */ - object Gone : SceneKey("gone") + @JvmField val Gone = SceneKey("gone") /** The lockscreen is the scene that shows when the device is locked. */ - object Lockscreen : SceneKey("lockscreen") + @JvmField val Lockscreen = SceneKey("lockscreen") /** The quick settings scene shows the quick setting tiles. */ - object QuickSettings : SceneKey("quick_settings") + @JvmField val QuickSettings = SceneKey("quick_settings") /** * The shade is the scene whose primary purpose is to show a scrollable list of notifications. */ - object Shade : SceneKey("shade") - - override fun toString(): String { - return loggingName - } + @JvmField val Shade = SceneKey("shade") } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt index 926878c1870e..b91dd0451808 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt @@ -16,6 +16,8 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.TransitionKey + /** * Defines all known named transitions. * diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt index f2697b4e1c1e..7c31ca269eb9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt @@ -31,6 +31,7 @@ import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import com.android.compose.animation.scene.SceneKey import com.android.compose.theme.PlatformTheme import com.android.internal.policy.ScreenDecorationsUtils import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation @@ -42,7 +43,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSourceDelegator -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.ui.composable.ComposableScene import com.android.systemui.scene.ui.composable.SceneContainer import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt index 22645c4532f6..ea19020d84d4 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt @@ -71,7 +71,6 @@ open class WindowRootView( override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets? { val insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()) - val displayCutout = rootWindowInsets.displayCutout if (fitsSystemWindows) { val paddingChanged = insets.top != paddingTop || insets.bottom != paddingBottom @@ -79,23 +78,22 @@ open class WindowRootView( if (paddingChanged) { setPadding(0, 0, 0, 0) } - - val pairInsets: Pair<Int, Int> = - layoutInsetsController.getinsets(windowInsets, displayCutout) - leftInset = pairInsets.first - rightInset = pairInsets.second - applyMargins() } else { val changed = paddingLeft != 0 || paddingRight != 0 || paddingTop != 0 || paddingBottom != 0 if (changed) { setPadding(0, 0, 0, 0) } - - leftInset = 0 - rightInset = 0 } + leftInset = 0 + rightInset = 0 + val displayCutout = rootWindowInsets.displayCutout + val pairInsets: Pair<Int, Int> = + layoutInsetsController.getinsets(windowInsets, displayCutout) + leftInset = pairInsets.first + rightInset = pairInsets.second + applyMargins() return windowInsets } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index 91861aa5c29a..231b28424691 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -17,13 +17,14 @@ package com.android.systemui.scene.ui.viewmodel import android.view.MotionEvent +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -96,10 +97,10 @@ constructor( fun canChangeScene(toScene: SceneKey): Boolean { val interactionTypeOrNull = when (toScene) { - SceneKey.Bouncer -> Classifier.BOUNCER_UNLOCK - SceneKey.Gone -> Classifier.UNLOCK - SceneKey.Shade -> Classifier.NOTIFICATION_DRAG_DOWN - SceneKey.QuickSettings -> Classifier.QUICK_SETTINGS + Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK + Scenes.Gone -> Classifier.UNLOCK + Scenes.Shade -> Classifier.NOTIFICATION_DRAG_DOWN + Scenes.QuickSettings -> Classifier.QUICK_SETTINGS else -> null } @@ -109,7 +110,7 @@ constructor( val isFalseTouch = falsingInteractor.isFalseTouch(interactionType) // Only enforce falsing if moving from the lockscreen scene to a new scene. - val fromLockscreenScene = currentScene.value == SceneKey.Lockscreen + val fromLockscreenScene = currentScene.value == Scenes.Lockscreen !fromLockscreenScene || !isFalseTouch } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt new file mode 100644 index 000000000000..2294fc0be520 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2024 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.screenshot + +import android.animation.Animator +import android.app.Notification +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.view.Display +import android.view.LayoutInflater +import android.view.ScrollCaptureResponse +import android.view.View +import android.view.ViewTreeObserver +import android.view.WindowInsets +import android.window.OnBackInvokedDispatcher +import com.android.internal.logging.UiEventLogger +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.res.R + +/** + * Legacy implementation of screenshot view methods. Just proxies the calls down into the original + * ScreenshotView. + */ +class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy { + override val view: ScreenshotView = + LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView + override val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener + override val screenshotPreview: View + + override var defaultDisplay: Int = Display.DEFAULT_DISPLAY + set(value) { + view.setDefaultDisplay(value) + } + override var defaultTimeoutMillis: Long = 6000 + set(value) { + view.setDefaultTimeoutMillis(value) + } + override var onKeyListener: View.OnKeyListener? = null + set(value) { + view.setOnKeyListener(value) + } + override var flags: FeatureFlags? = null + set(value) { + view.setFlags(value) + } + override var packageName: String = "" + set(value) { + view.setPackageName(value) + } + override var logger: UiEventLogger? = null + set(value) { + view.setUiEventLogger(value) + } + override var callbacks: ScreenshotView.ScreenshotViewCallback? = null + set(value) { + view.setCallbacks(value) + } + override var screenshot: ScreenshotData? = null + set(value) { + view.setScreenshot(value) + } + + override val isAttachedToWindow + get() = view.isAttachedToWindow + override val isDismissing + get() = view.isDismissing + override val isPendingSharedTransition + get() = view.isPendingSharedTransition + + init { + internalInsetsListener = view + screenshotPreview = view.screenshotPreview + } + + override fun reset() = view.reset() + override fun updateInsets(insets: WindowInsets) = view.updateInsets(insets) + override fun updateOrientation(insets: WindowInsets) = view.updateOrientation(insets) + + override fun badgeScreenshot(userBadgedIcon: Drawable) = view.badgeScreenshot(userBadgedIcon) + + override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator = + view.createScreenshotDropInAnimation(screenRect, showFlash) + + override fun addQuickShareChip(quickShareAction: Notification.Action) = + view.addQuickShareChip(quickShareAction) + + override fun setChipIntents(imageData: ScreenshotController.SavedImageData) = + view.setChipIntents(imageData) + + override fun animateDismissal() = view.animateDismissal() + + override fun showScrollChip(packageName: String, onClick: Runnable) = + view.showScrollChip(packageName, onClick) + + override fun hideScrollChip() = view.hideScrollChip() + + override fun prepareScrollingTransition( + response: ScrollCaptureResponse, + screenBitmap: Bitmap, + newScreenshot: Bitmap, + screenshotTakenInPortrait: Boolean + ) = + view.prepareScrollingTransition( + response, + screenBitmap, + newScreenshot, + screenshotTakenInPortrait + ) + + override fun startLongScreenshotTransition( + transitionDestination: Rect, + onTransitionEnd: Runnable, + longScreenshot: ScrollCaptureController.LongScreenshot + ) = view.startLongScreenshotTransition(transitionDestination, onTransitionEnd, longScreenshot) + + override fun restoreNonScrollingUi() = view.restoreNonScrollingUi() + + override fun stopInputListening() = view.stopInputListening() + + override fun requestFocus() { + view.requestFocus() + } + + override fun announceForAccessibility(string: String) = view.announceForAccessibility(string) + + override fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener) = + view.addOnAttachStateChangeListener(listener) + + override fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher? = + view.findOnBackInvokedDispatcher() + + override fun getViewTreeObserver(): ViewTreeObserver = view.viewTreeObserver + + override fun post(runnable: Runnable) { + view.post(runnable) + } + + class Factory : ScreenshotViewProxy.Factory { + override fun getProxy(context: Context): ScreenshotViewProxy { + return LegacyScreenshotViewProxy(context) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index ee3e7ba9e8b3..13448d258a2c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -65,7 +65,6 @@ import android.view.Display; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.ScrollCaptureResponse; @@ -165,7 +164,7 @@ public class ScreenshotController { /** * Structure returned by the SaveImageInBackgroundTask */ - static class SavedImageData { + public static class SavedImageData { public Uri uri; public List<Notification.Action> smartActions; public Notification.Action quickShareAction; @@ -237,6 +236,7 @@ public class ScreenshotController { private final WindowContext mContext; private final FeatureFlags mFlags; + private final ScreenshotViewProxy mViewProxy; private final ScreenshotNotificationsController mNotificationsController; private final ScreenshotSmartActions mScreenshotSmartActions; private final UiEventLogger mUiEventLogger; @@ -272,7 +272,6 @@ public class ScreenshotController { respondToKeyDismissal(); }; - private ScreenshotView mScreenshotView; private final MessageContainerController mMessageContainerController; private Bitmap mScreenBitmap; private SaveImageInBackgroundTask mSaveInBgTask; @@ -305,6 +304,7 @@ public class ScreenshotController { ScreenshotController( Context context, FeatureFlags flags, + ScreenshotViewProxy.Factory viewProxyFactory, ScreenshotSmartActions screenshotSmartActions, ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory, ScrollCaptureClient scrollCaptureClient, @@ -360,6 +360,8 @@ public class ScreenshotController { mMessageContainerController = messageContainerController; mAssistContentRequester = assistContentRequester; + mViewProxy = viewProxyFactory.getProxy(mContext); + mAccessibilityManager = AccessibilityManager.getInstance(mContext); // Setup the window that we are going to use @@ -461,7 +463,7 @@ public class ScreenshotController { // The window is focusable by default setWindowFocusable(true); - mScreenshotView.requestFocus(); + mViewProxy.requestFocus(); enqueueScrollCaptureRequest(screenshot.getUserHandle()); @@ -485,10 +487,10 @@ public class ScreenshotController { mMessageContainerController.onScreenshotTaken(screenshot); }); - mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon( + mViewProxy.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon( mContext.getDrawable(R.drawable.overlay_badge_background), screenshot.getUserHandle())); - mScreenshotView.setScreenshot(screenshot); + mViewProxy.setScreenshot(screenshot); // ignore system bar insets for the purpose of window layout mWindow.getDecorView().setOnApplyWindowInsetsListener( @@ -503,31 +505,31 @@ public class ScreenshotController { void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) { withWindowAttached(() -> { if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) { - mScreenshotView.announceForAccessibility(mContext.getResources().getString( + mViewProxy.announceForAccessibility(mContext.getResources().getString( R.string.screenshot_saving_work_profile_title)); } else { - mScreenshotView.announceForAccessibility( + mViewProxy.announceForAccessibility( mContext.getResources().getString(R.string.screenshot_saving_title)); } }); - mScreenshotView.reset(); + mViewProxy.reset(); - if (mScreenshotView.isAttachedToWindow()) { + if (mViewProxy.isAttachedToWindow()) { // if we didn't already dismiss for another reason - if (!mScreenshotView.isDismissing()) { + if (!mViewProxy.isDismissing()) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0, oldPackageName); } if (DEBUG_WINDOW) { Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. " - + "(dismissing=" + mScreenshotView.isDismissing() + ")"); + + "(dismissing=" + mViewProxy.isDismissing() + ")"); } } - mScreenshotView.setPackageName(mPackageName); + mViewProxy.setPackageName(mPackageName); - mScreenshotView.updateOrientation( + mViewProxy.updateOrientation( mWindowManager.getCurrentWindowMetrics().getWindowInsets()); } @@ -539,7 +541,7 @@ public class ScreenshotController { Log.d(TAG, "dismissScreenshot"); } // If we're already animating out, don't restart the animation - if (mScreenshotView.isDismissing()) { + if (mViewProxy.isDismissing()) { if (DEBUG_DISMISS) { Log.v(TAG, "Already dismissing, ignoring duplicate command"); } @@ -547,11 +549,11 @@ public class ScreenshotController { } mUiEventLogger.log(event, 0, mPackageName); mScreenshotHandler.cancelTimeout(); - mScreenshotView.animateDismissal(); + mViewProxy.animateDismissal(); } boolean isPendingSharedTransition() { - return mScreenshotView.isPendingSharedTransition(); + return mViewProxy.isPendingSharedTransition(); } // Any cleanup needed when the service is being destroyed. @@ -576,7 +578,7 @@ public class ScreenshotController { private void releaseMediaPlayer() { if (mScreenshotSoundController == null) return; - mScreenshotSoundController.releaseScreenshotSound(); + mScreenshotSoundController.releaseScreenshotSoundAsync(); } private void respondToKeyDismissal() { @@ -591,18 +593,15 @@ public class ScreenshotController { Log.d(TAG, "reloadAssets()"); } - // Inflate the screenshot layout - mScreenshotView = (ScreenshotView) - LayoutInflater.from(mContext).inflate(R.layout.screenshot, null); - mMessageContainerController.setView(mScreenshotView); - mScreenshotView.addOnAttachStateChangeListener( + mMessageContainerController.setView(mViewProxy.getView()); + mViewProxy.addOnAttachStateChangeListener( new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(@NonNull View v) { if (DEBUG_INPUT) { Log.d(TAG, "Registering Predictive Back callback"); } - mScreenshotView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback( + mViewProxy.findOnBackInvokedDispatcher().registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback); } @@ -611,11 +610,12 @@ public class ScreenshotController { if (DEBUG_INPUT) { Log.d(TAG, "Unregistering Predictive Back callback"); } - mScreenshotView.findOnBackInvokedDispatcher() + mViewProxy.findOnBackInvokedDispatcher() .unregisterOnBackInvokedCallback(mOnBackInvokedCallback); } }); - mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() { + mViewProxy.setLogger(mUiEventLogger); + mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() { @Override public void onUserInteraction() { if (DEBUG_INPUT) { @@ -640,11 +640,12 @@ public class ScreenshotController { // TODO(159460485): Remove this when focus is handled properly in the system setWindowFocusable(false); } - }, mFlags); - mScreenshotView.setDefaultDisplay(mDisplayId); - mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis()); + }); + mViewProxy.setFlags(mFlags); + mViewProxy.setDefaultDisplay(mDisplayId); + mViewProxy.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis()); - mScreenshotView.setOnKeyListener((v, keyCode, event) -> { + mViewProxy.setOnKeyListener((v, keyCode, event) -> { if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) { if (DEBUG_INPUT) { Log.d(TAG, "onKeyEvent: " + keyCode); @@ -658,23 +659,24 @@ public class ScreenshotController { if (DEBUG_WINDOW) { Log.d(TAG, "adding OnComputeInternalInsetsListener"); } - mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView); + mViewProxy.getViewTreeObserver().addOnComputeInternalInsetsListener( + mViewProxy.getInternalInsetsListener()); if (DEBUG_WINDOW) { - Log.d(TAG, "setContentView: " + mScreenshotView); + Log.d(TAG, "setContentView: " + mViewProxy.getView()); } - setContentView(mScreenshotView); + setContentView(mViewProxy.getView()); } private void prepareAnimation(Rect screenRect, boolean showFlash, Runnable onAnimationComplete) { - mScreenshotView.getViewTreeObserver().addOnPreDrawListener( + mViewProxy.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (DEBUG_WINDOW) { Log.d(TAG, "onPreDraw: startAnimation"); } - mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this); + mViewProxy.getViewTreeObserver().removeOnPreDrawListener(this); startAnimation(screenRect, showFlash, onAnimationComplete); return true; } @@ -694,13 +696,13 @@ public class ScreenshotController { if (mConfigChanges.applyNewConfig(mContext.getResources())) { // Hide the scroll chip until we know it's available in this // orientation - mScreenshotView.hideScrollChip(); + mViewProxy.hideScrollChip(); // Delay scroll capture eval a bit to allow the underlying activity // to set up in the new orientation. mScreenshotHandler.postDelayed(() -> { requestScrollCapture(owner); }, 150); - mScreenshotView.updateInsets( + mViewProxy.updateInsets( mWindowManager.getCurrentWindowMetrics().getWindowInsets()); // Screenshot animation calculations won't be valid anymore, // so just end @@ -759,16 +761,16 @@ public class ScreenshotController { + mLastScrollCaptureResponse.getWindowTitle() + "]"); final ScrollCaptureResponse response = mLastScrollCaptureResponse; - mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> { + mViewProxy.showScrollChip(response.getPackageName(), /* onClick */ () -> { DisplayMetrics displayMetrics = new DisplayMetrics(); getDisplay().getRealMetrics(displayMetrics); Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId, new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)); - mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot, + mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot, mScreenshotTakenInPortrait); // delay starting scroll capture to make sure the scrim is up before the app moves - mScreenshotView.post(() -> runBatchScrollCapture(response, owner)); + mViewProxy.post(() -> runBatchScrollCapture(response, owner)); }); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "requestScrollCapture failed", e); @@ -794,19 +796,19 @@ public class ScreenshotController { return; } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "Exception", e); - mScreenshotView.restoreNonScrollingUi(); + mViewProxy.restoreNonScrollingUi(); return; } if (longScreenshot.getHeight() == 0) { - mScreenshotView.restoreNonScrollingUi(); + mViewProxy.restoreNonScrollingUi(); return; } mLongScreenshotHolder.setLongScreenshot(longScreenshot); mLongScreenshotHolder.setTransitionDestinationCallback( (transitionDestination, onTransitionEnd) -> { - mScreenshotView.startLongScreenshotTransition( + mViewProxy.startLongScreenshotTransition( transitionDestination, onTransitionEnd, longScreenshot); // TODO: Do this via ActionIntentExecutor instead. @@ -882,16 +884,14 @@ public class ScreenshotController { } mWindowManager.removeViewImmediate(decorView); } - // Ensure that we remove the input monitor - if (mScreenshotView != null) { - mScreenshotView.stopInputListening(); - } + + mViewProxy.stopInputListening(); } private void playCameraSoundIfNeeded() { if (mScreenshotSoundController == null) return; // the controller is not-null only on the default display controller - mScreenshotSoundController.playCameraSound(); + mScreenshotSoundController.playScreenshotSoundAsync(); } /** @@ -932,7 +932,7 @@ public class ScreenshotController { } mScreenshotAnimation = - mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); + mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash); if (onAnimationComplete != null) { mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -975,7 +975,7 @@ public class ScreenshotController { }; Pair<ActivityOptions, ExitTransitionCoordinator> transition = ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null, - Pair.create(mScreenshotView.getScreenshotPreview(), + Pair.create(mViewProxy.getScreenshotPreview(), ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); return transition; @@ -999,7 +999,7 @@ public class ScreenshotController { mCurrentRequestCallback.onFinish(); mCurrentRequestCallback = null; } - mScreenshotView.reset(); + mViewProxy.reset(); removeWindow(); mScreenshotHandler.cancelTimeout(); } @@ -1067,7 +1067,7 @@ public class ScreenshotController { } private void doPostAnimation(ScreenshotController.SavedImageData imageData) { - mScreenshotView.setChipIntents(imageData); + mViewProxy.setChipIntents(imageData); } /** @@ -1084,11 +1084,11 @@ public class ScreenshotController { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - mScreenshotView.addQuickShareChip(quickShareData.quickShareAction); + mViewProxy.addQuickShareChip(quickShareData.quickShareAction); } }); } else { - mScreenshotView.addQuickShareChip(quickShareData.quickShareAction); + mViewProxy.addQuickShareChip(quickShareData.quickShareAction); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt index 2c0bddecc58e..d3a7fc4a3e4a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt @@ -21,22 +21,34 @@ import android.util.Log import com.android.app.tracing.coroutines.async import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background -import com.google.errorprone.annotations.CanIgnoreReturnValue import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout /** Controls sound reproduction after a screenshot is taken. */ interface ScreenshotSoundController { /** Reproduces the camera sound. */ - @CanIgnoreReturnValue fun playCameraSound(): Deferred<Unit> + suspend fun playScreenshotSound() - /** Releases the sound. [playCameraSound] behaviour is undefined after this has been called. */ - @CanIgnoreReturnValue fun releaseScreenshotSound(): Deferred<Unit> + /** + * Releases the sound. [playScreenshotSound] behaviour is undefined after this has been called. + */ + suspend fun releaseScreenshotSound() + + /** Reproduces the camera sound. Used for compatibility with Java code. */ + fun playScreenshotSoundAsync() + + /** + * Releases the sound. [playScreenshotSound] behaviour is undefined after this has been called. + * Used for compatibility with Java code. + */ + fun releaseScreenshotSoundAsync() } class ScreenshotSoundControllerImpl @@ -47,8 +59,8 @@ constructor( @Background private val bgDispatcher: CoroutineDispatcher ) : ScreenshotSoundController { - val player: Deferred<MediaPlayer?> = - coroutineScope.async("loadCameraSound", bgDispatcher) { + private val player: Deferred<MediaPlayer?> = + coroutineScope.async("loadScreenshotSound", bgDispatcher) { try { soundProvider.getScreenshotSound() } catch (e: IllegalStateException) { @@ -57,8 +69,8 @@ constructor( } } - override fun playCameraSound(): Deferred<Unit> { - return coroutineScope.async("playCameraSound", bgDispatcher) { + override suspend fun playScreenshotSound() { + withContext(bgDispatcher) { try { player.await()?.start() } catch (e: IllegalStateException) { @@ -68,8 +80,8 @@ constructor( } } - override fun releaseScreenshotSound(): Deferred<Unit> { - return coroutineScope.async("releaseScreenshotSound", bgDispatcher) { + override suspend fun releaseScreenshotSound() { + withContext(bgDispatcher) { try { withTimeout(1.seconds) { player.await()?.release() } } catch (e: TimeoutCancellationException) { @@ -79,6 +91,14 @@ constructor( } } + override fun playScreenshotSoundAsync() { + coroutineScope.launch { playScreenshotSound() } + } + + override fun releaseScreenshotSoundAsync() { + coroutineScope.launch { releaseScreenshotSound() } + } + private companion object { const val TAG = "ScreenshotSoundControllerImpl" } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index be30a1576b21..8a8766dbab94 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -102,7 +102,7 @@ import java.util.ArrayList; public class ScreenshotView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { - interface ScreenshotViewCallback { + public interface ScreenshotViewCallback { void onUserInteraction(); void onAction(Intent intent, UserHandle owner, boolean overrideTransition); @@ -426,15 +426,15 @@ public class ScreenshotView extends FrameLayout implements return mScreenshotPreview; } - /** - * Set up the logger and callback on dismissal. - * - * Note: must be called before any other (non-constructor) method or null pointer exceptions - * may occur. - */ - void init(UiEventLogger uiEventLogger, ScreenshotViewCallback callbacks, FeatureFlags flags) { + void setUiEventLogger(UiEventLogger uiEventLogger) { mUiEventLogger = uiEventLogger; + } + + void setCallbacks(ScreenshotViewCallback callbacks) { mCallbacks = callbacks; + } + + void setFlags(FeatureFlags flags) { mFlags = flags; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt new file mode 100644 index 000000000000..0064521bd3a4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 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.screenshot + +import android.animation.Animator +import android.app.Notification +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.view.ScrollCaptureResponse +import android.view.View +import android.view.View.OnKeyListener +import android.view.ViewGroup +import android.view.ViewTreeObserver +import android.view.WindowInsets +import android.window.OnBackInvokedDispatcher +import com.android.internal.logging.UiEventLogger +import com.android.systemui.flags.FeatureFlags + +/** Abstraction of the surface between ScreenshotController and ScreenshotView */ +interface ScreenshotViewProxy { + val view: ViewGroup + val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener + val screenshotPreview: View + + var defaultDisplay: Int + var defaultTimeoutMillis: Long + var onKeyListener: OnKeyListener? + var flags: FeatureFlags? + var packageName: String + var logger: UiEventLogger? + var callbacks: ScreenshotView.ScreenshotViewCallback? + var screenshot: ScreenshotData? + + val isAttachedToWindow: Boolean + val isDismissing: Boolean + val isPendingSharedTransition: Boolean + + fun reset() + fun updateInsets(insets: WindowInsets) + fun updateOrientation(insets: WindowInsets) + fun badgeScreenshot(userBadgedIcon: Drawable) + fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator + fun addQuickShareChip(quickShareAction: Notification.Action) + fun setChipIntents(imageData: ScreenshotController.SavedImageData) + fun animateDismissal() + + fun showScrollChip(packageName: String, onClick: Runnable) + fun hideScrollChip() + fun prepareScrollingTransition( + response: ScrollCaptureResponse, + screenBitmap: Bitmap, + newScreenshot: Bitmap, + screenshotTakenInPortrait: Boolean + ) + fun startLongScreenshotTransition( + transitionDestination: Rect, + onTransitionEnd: Runnable, + longScreenshot: ScrollCaptureController.LongScreenshot + ) + fun restoreNonScrollingUi() + + fun stopInputListening() + fun requestFocus() + fun announceForAccessibility(string: String) + fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener) + fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher? + fun getViewTreeObserver(): ViewTreeObserver + fun post(runnable: Runnable) + + interface Factory { + fun getProxy(context: Context): ScreenshotViewProxy + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index bb34ede2cf5e..8a2678c8ab09 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -75,7 +75,7 @@ public class ScrollCaptureController { private String mWindowOwner; private volatile boolean mCancelled; - static class LongScreenshot { + public static class LongScreenshot { private final ImageTileSet mImageTileSet; private final Session mSession; // TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java index 3797b8b41e5a..a00c81d43b9e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java @@ -20,6 +20,7 @@ import android.app.Service; import com.android.systemui.screenshot.ImageCapture; import com.android.systemui.screenshot.ImageCaptureImpl; +import com.android.systemui.screenshot.LegacyScreenshotViewProxy; import com.android.systemui.screenshot.RequestProcessor; import com.android.systemui.screenshot.ScreenshotPolicy; import com.android.systemui.screenshot.ScreenshotPolicyImpl; @@ -29,12 +30,14 @@ import com.android.systemui.screenshot.ScreenshotSoundController; import com.android.systemui.screenshot.ScreenshotSoundControllerImpl; import com.android.systemui.screenshot.ScreenshotSoundProvider; import com.android.systemui.screenshot.ScreenshotSoundProviderImpl; +import com.android.systemui.screenshot.ScreenshotViewProxy; import com.android.systemui.screenshot.TakeScreenshotService; import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService; import com.android.systemui.screenshot.appclips.AppClipsService; import dagger.Binds; import dagger.Module; +import dagger.Provides; import dagger.multibindings.ClassKey; import dagger.multibindings.IntoMap; @@ -81,4 +84,9 @@ public abstract class ScreenshotModule { @Binds abstract ScreenshotSoundController bindScreenshotSoundController( ScreenshotSoundControllerImpl screenshotSoundProviderImpl); + + @Provides + static ScreenshotViewProxy.Factory providesScreenshotViewProxyFactory() { + return new LegacyScreenshotViewProxy.Factory(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 1876f4739c87..1566de58ef3a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1288,9 +1288,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mView.getContext().getDisplay()); mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); mKeyguardStatusViewController.init(); + } - mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled); - mKeyguardStatusViewController.getView().addOnLayoutChangeListener( + mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled); + mKeyguardStatusViewController.getView().addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { int oldHeight = oldBottom - oldTop; if (v.getHeight() != oldHeight) { @@ -1298,8 +1299,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } }); - updateClockAppearance(); - } + updateClockAppearance(); } @Override @@ -1326,9 +1326,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void onSplitShadeEnabledChanged() { mShadeLog.logSplitShadeChanged(mSplitShadeEnabled); - if (!migrateClocksToBlueprint()) { - mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled); - } + mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled); // Reset any left over overscroll state. It is a rare corner case but can happen. mQsController.setOverScrollAmount(0); mScrimController.setNotificationsOverScrollAmount(0); @@ -1443,13 +1441,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), mStatusBarStateController.getInterpolatedDozeAmount()); - if (!migrateClocksToBlueprint()) { - mKeyguardStatusViewController.setKeyguardStatusViewVisibility( - mBarState, - false, - false, - mBarState); - } + mKeyguardStatusViewController.setKeyguardStatusViewVisibility( + mBarState, + false, + false, + mBarState); if (mKeyguardQsUserSwitchController != null) { mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility( mBarState, @@ -1669,11 +1665,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.setLockscreenClockY( mClockPositionAlgorithm.getExpandedPreferredClockY()); } - if (!(migrateClocksToBlueprint() || keyguardBottomAreaRefactor())) { + if (keyguardBottomAreaRefactor()) { + mKeyguardInteractor.setClockPosition( + mClockPositionResult.clockX, mClockPositionResult.clockY); + } else { mKeyguardBottomAreaInteractor.setClockPosition( mClockPositionResult.clockX, mClockPositionResult.clockY); } - boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange; @@ -1751,11 +1749,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private void updateKeyguardStatusViewAlignment(boolean animate) { + boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); + ConstraintLayout layout; if (migrateClocksToBlueprint()) { - return; + layout = mKeyguardViewConfigurator.getKeyguardRootView(); + } else { + layout = mNotificationContainerParent; } - boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); - ConstraintLayout layout = mNotificationContainerParent; mKeyguardStatusViewController.updateAlignment( layout, mSplitShadeEnabled, shouldBeCentered, animate); mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); @@ -2045,7 +2045,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mView.animate().cancel(); } - @Override public void expandToQs() { if (mQsController.isExpansionEnabled()) { mQsController.setExpandImmediate(true); @@ -2812,7 +2811,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mQsController.setListening(listening); } - @Override public void expand(boolean animate) { if (isFullyCollapsed() || isCollapsing()) { mInstantExpanding = true; @@ -3318,9 +3316,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump /** Updates the views to the initial state for the fold to AOD animation. */ @Override public void prepareFoldToAodAnimation() { - if (migrateClocksToBlueprint()) { - return; - } // Force show AOD UI even if we are not locked showAodUi(); @@ -3342,9 +3337,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void startFoldToAodAnimation(Runnable startAction, Runnable endAction, Runnable cancelAction) { - if (migrateClocksToBlueprint()) { - return; - } final ViewPropertyAnimator viewAnimator = mView.animate(); viewAnimator.cancel(); viewAnimator @@ -3380,9 +3372,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump /** Cancels fold to AOD transition and resets view state. */ @Override public void cancelFoldToAodAnimation() { - if (migrateClocksToBlueprint()) { - return; - } cancelAnimation(); resetAlpha(); resetTranslation(); @@ -4457,13 +4446,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } } - if (!migrateClocksToBlueprint()) { - mKeyguardStatusViewController.setKeyguardStatusViewVisibility( - statusBarState, - keyguardFadingAway, - goingToFullShade, - mBarState); - } + mKeyguardStatusViewController.setKeyguardStatusViewVisibility( + statusBarState, + keyguardFadingAway, + goingToFullShade, + mBarState); if (!keyguardBottomAreaRefactor()) { setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 8f9cef37dac7..f7fed537a167 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -60,7 +60,6 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.res.R; -import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.ui.view.WindowRootViewComponent; import com.android.systemui.settings.UserTracker; @@ -507,7 +506,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private void applyFitsSystemWindows(NotificationShadeWindowState state) { boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded(); - if (!SceneContainerFlag.isEnabled() && mWindowRootView != null + if (mWindowRootView != null && mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) { mWindowRootView.setFitsSystemWindows(fitsSystemWindows); mWindowRootView.requestApplyInsets(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt index efd9ce0d08b2..27168a799086 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt @@ -17,6 +17,7 @@ package com.android.systemui.shade import android.view.MotionEvent +import com.android.compose.animation.scene.SceneKey import com.android.systemui.assist.AssistManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -25,7 +26,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.ShadeTouchLog import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.CollapseShadeInstantly import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse import com.android.systemui.shade.ShadeController.ShadeVisibilityListener @@ -121,7 +122,7 @@ constructor( // release focus immediately to kick off focus change transition notificationShadeWindowController.setNotificationShadeFocusable(false) notificationStackScrollLayout.cancelExpandHelper() - sceneInteractor.changeScene(SceneKey.Shade, "ShadeController.animateExpandShade") + sceneInteractor.changeScene(Scenes.Shade, "ShadeController.animateExpandShade") if (delayed) { scope.launch { delay(125) @@ -148,9 +149,9 @@ constructor( private fun getCollapseDestinationScene(): SceneKey { return if (deviceEntryInteractor.isDeviceEntered.value) { - SceneKey.Gone + Scenes.Gone } else { - SceneKey.Lockscreen + Scenes.Lockscreen } } @@ -188,11 +189,11 @@ constructor( } override fun expandToNotifications() { - sceneInteractor.changeScene(SceneKey.Shade, "ShadeController.animateExpandShade") + sceneInteractor.changeScene(Scenes.Shade, "ShadeController.animateExpandShade") } override fun expandToQs() { - sceneInteractor.changeScene(SceneKey.QuickSettings, "ShadeController.animateExpandQs") + sceneInteractor.changeScene(Scenes.QuickSettings, "ShadeController.animateExpandQs") } override fun setVisibilityListener(listener: ShadeVisibilityListener) { @@ -242,7 +243,7 @@ constructor( } override fun isExpandedVisible(): Boolean { - return sceneInteractor.currentScene.value != SceneKey.Gone + return sceneInteractor.currentScene.value != Scenes.Gone } override fun onStatusBarTouch(event: MotionEvent) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt index 1f4a0f1f1b91..7a1637eeab15 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt @@ -31,12 +31,6 @@ import java.util.function.Consumer * @see NotificationPanelViewController */ interface ShadeViewController { - /** Expand the shade either animated or instantly. */ - fun expand(animate: Boolean) - - /** Animates to an expanded shade with QS expanded. If the shade starts expanded, expands QS. */ - fun expandToQs() - /** Returns whether the shade is expanding or collapsing itself or quick settings. */ val isExpandingOrCollapsing: Boolean diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt index d756f026e21d..3be3f6b1441d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt @@ -28,8 +28,6 @@ import javax.inject.Inject /** Empty implementation of ShadeViewController for variants with no shade. */ class ShadeViewControllerEmptyImpl @Inject constructor() : ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor { - override fun expand(animate: Boolean) {} - override fun expandToQs() {} override fun expandToNotifications() {} override val isExpandingOrCollapsing: Boolean = false override val isExpanded: Boolean = false diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt index 1ee6d3845553..eaac8ae9dd3a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt @@ -16,10 +16,10 @@ package com.android.systemui.shade.domain.interactor +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeAnimationRepository import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -44,10 +44,10 @@ constructor( is ObservableTransitionState.Idle -> flowOf(false) is ObservableTransitionState.Transition -> if ( - (state.fromScene == SceneKey.Shade && - state.toScene != SceneKey.QuickSettings) || - (state.fromScene == SceneKey.QuickSettings && - state.toScene != SceneKey.Shade) + (state.fromScene == Scenes.Shade && + state.toScene != Scenes.QuickSettings) || + (state.fromScene == Scenes.QuickSettings && + state.toScene != Scenes.Shade) ) { state.isUserInputOngoing.map { !it } } else { diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt index a2e25983e68f..3a8ba7a0696b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt @@ -18,7 +18,7 @@ package com.android.systemui.shade.domain.interactor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -36,12 +36,12 @@ constructor( val key = if (fullyCollapse) { if (deviceEntryInteractor.isDeviceEntered.value) { - SceneKey.Gone + Scenes.Gone } else { - SceneKey.Lockscreen + Scenes.Lockscreen } } else { - SceneKey.Shade + Scenes.Shade } sceneInteractor.changeScene(key, "animateCollapseQs") } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt index 08f2c402f9e9..67cac3d4111c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt @@ -16,11 +16,12 @@ package com.android.systemui.shade.domain.interactor +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -45,9 +46,9 @@ constructor( sceneInteractor: SceneInteractor, sharedNotificationContainerInteractor: SharedNotificationContainerInteractor, ) : BaseShadeInteractor { - override val shadeExpansion: Flow<Float> = sceneBasedExpansion(sceneInteractor, SceneKey.Shade) + override val shadeExpansion: Flow<Float> = sceneBasedExpansion(sceneInteractor, Scenes.Shade) - private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings) + private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings) override val qsExpansion: StateFlow<Float> = combine( @@ -75,7 +76,7 @@ constructor( when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> - state.toScene == SceneKey.QuickSettings && state.fromScene != SceneKey.Shade + state.toScene == Scenes.QuickSettings && state.fromScene != Scenes.Shade } } .distinctUntilChanged() @@ -84,7 +85,7 @@ constructor( sceneInteractor.transitionState .map { state -> when (state) { - is ObservableTransitionState.Idle -> state.scene == SceneKey.QuickSettings + is ObservableTransitionState.Idle -> state.scene == Scenes.QuickSettings is ObservableTransitionState.Transition -> false } } @@ -100,10 +101,10 @@ constructor( .stateIn(scope, SharingStarted.Eagerly, false) override val isUserInteractingWithShade: Flow<Boolean> = - sceneBasedInteracting(sceneInteractor, SceneKey.Shade) + sceneBasedInteracting(sceneInteractor, Scenes.Shade) override val isUserInteractingWithQs: Flow<Boolean> = - sceneBasedInteracting(sceneInteractor, SceneKey.QuickSettings) + sceneBasedInteracting(sceneInteractor, Scenes.QuickSettings) /** * Returns a flow that uses scene transition progress to and from a scene that is pulled down diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt index 21a782e43b78..1f78ae8b6e99 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt @@ -19,7 +19,7 @@ package com.android.systemui.shade.domain.interactor import com.android.keyguard.LockIconViewController import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ShadeLockscreenInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -87,7 +87,7 @@ constructor( private fun changeToShadeScene() { sceneInteractor.changeScene( - SceneKey.Shade, + Scenes.Shade, "ShadeLockscreenInteractorImpl.expandToNotifications", ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt index 38358a8f244e..c9aa51c31060 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -16,12 +16,13 @@ package com.android.systemui.shade.ui.viewmodel +import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.qs.ui.adapter.QSSceneAdapter -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -67,7 +68,7 @@ constructor( /** Whether or not the shade container should be clickable. */ val isClickable: StateFlow<Boolean> = upDestinationSceneKey - .map { it == SceneKey.Lockscreen } + .map { it == Scenes.Lockscreen } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), @@ -82,9 +83,9 @@ constructor( canSwipeToDismiss: Boolean?, ): SceneKey { return when { - canSwipeToDismiss == true -> SceneKey.Lockscreen - isUnlocked -> SceneKey.Gone - else -> SceneKey.Lockscreen + canSwipeToDismiss == true -> Scenes.Lockscreen + isUnlocked -> Scenes.Gone + else -> Scenes.Lockscreen } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index a4741a509d72..ef4e5308e18e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java @@ -482,42 +482,42 @@ public final class KeyboardShortcutListSearch { context.getString(R.string.keyboard_shortcut_group_system), new ArrayList<>()); List<ShortcutKeyGroupMultiMappingInfo> infoList = Arrays.asList( - /* Access notification shade: Meta + N */ + /* Access list of all apps and search (i.e. Search/Launcher): Meta */ new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_access_notification_shade), + context.getString(R.string.group_system_access_all_apps_search), Arrays.asList( - Pair.create(KeyEvent.KEYCODE_N, KeyEvent.META_META_ON))), - /* Take a full screenshot: Meta + Ctrl + S */ + Pair.create(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON))), + /* Access home screen: Meta + H, Meta + Enter */ new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_full_screenshot), + context.getString(R.string.group_system_access_home_screen), Arrays.asList( - Pair.create( - KeyEvent.KEYCODE_S, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))), - /* Access list of system / apps shortcuts: Meta + / */ + Pair.create(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON), + Pair.create(KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON))), + /* Overview of open apps: Meta + Tab */ new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_access_system_app_shortcuts), + context.getString(R.string.group_system_overview_open_apps), Arrays.asList( - Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))), + Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_META_ON))), /* Back: go back to previous state (back button) */ - /* Meta + Escape, Meta + Grave, Meta + backspace, Meta + left arrow */ + /* Meta + Escape, Meta + backspace, Meta + left arrow */ new ShortcutKeyGroupMultiMappingInfo( context.getString(R.string.group_system_go_back), Arrays.asList( Pair.create(KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_META_ON), Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON), Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))), - /* Access home screen: Meta + H, Meta + Enter */ + /* Take a full screenshot: Meta + Ctrl + S */ new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_access_home_screen), + context.getString(R.string.group_system_full_screenshot), Arrays.asList( - Pair.create(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON), - Pair.create(KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON))), - /* Overview of open apps: Meta + Tab */ + Pair.create( + KeyEvent.KEYCODE_S, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))), + /* Access list of system / apps shortcuts: Meta + / */ new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_overview_open_apps), + context.getString(R.string.group_system_access_system_app_shortcuts), Arrays.asList( - Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_META_ON))), + Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))), /* Cycle through recent apps (forward): Alt + Tab */ new ShortcutKeyGroupMultiMappingInfo( context.getString(R.string.group_system_cycle_forward), @@ -530,26 +530,16 @@ public final class KeyboardShortcutListSearch { Pair.create( KeyEvent.KEYCODE_TAB, KeyEvent.META_SHIFT_ON | KeyEvent.META_ALT_ON))), - /* Access list of all apps and search (i.e. Search/Launcher): Meta */ - new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_access_all_apps_search), - Arrays.asList( - Pair.create(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON))), /* Hide and (re)show taskbar: Meta + T */ new ShortcutKeyGroupMultiMappingInfo( context.getString(R.string.group_system_hide_reshow_taskbar), Arrays.asList( Pair.create(KeyEvent.KEYCODE_T, KeyEvent.META_META_ON))), - /* Access system settings: Meta + I */ - new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_access_system_settings), - Arrays.asList( - Pair.create(KeyEvent.KEYCODE_I, KeyEvent.META_META_ON))), - /* Access Google Assistant: Meta + A */ + /* Access notification shade: Meta + N */ new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_access_google_assistant), + context.getString(R.string.group_system_access_notification_shade), Arrays.asList( - Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))), + Pair.create(KeyEvent.KEYCODE_N, KeyEvent.META_META_ON))), /* Lock screen: Meta + L */ new ShortcutKeyGroupMultiMappingInfo( context.getString(R.string.group_system_lock_screen), @@ -561,7 +551,17 @@ public final class KeyboardShortcutListSearch { Arrays.asList( Pair.create( KeyEvent.KEYCODE_N, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))) + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))), + /* Access system settings: Meta + I */ + new ShortcutKeyGroupMultiMappingInfo( + context.getString(R.string.group_system_access_system_settings), + Arrays.asList( + Pair.create(KeyEvent.KEYCODE_I, KeyEvent.META_META_ON))), + /* Access Google Assistant: Meta + A */ + new ShortcutKeyGroupMultiMappingInfo( + context.getString(R.string.group_system_access_google_assistant), + Arrays.asList( + Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))) ); for (ShortcutKeyGroupMultiMappingInfo info : infoList) { systemGroup.addItem(info.getShortcutMultiMappingInfo()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 36fc9bb3a2da..e0dd7f0603e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -36,6 +36,7 @@ import android.view.animation.Interpolator; import androidx.annotation.NonNull; import com.android.app.animation.Interpolators; +import com.android.compose.animation.scene.SceneKey; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; @@ -49,7 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController.StateList import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.scene.shared.model.SceneKey; +import com.android.systemui.scene.shared.model.Scenes; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.policy.CallbackController; @@ -659,11 +660,11 @@ public class StatusBarStateControllerImpl implements } private static final Map<SceneKey, Integer> sStatusBarStateByLockedSceneKey = Map.of( - SceneKey.Lockscreen.INSTANCE, StatusBarState.KEYGUARD, - SceneKey.Bouncer.INSTANCE, StatusBarState.KEYGUARD, - SceneKey.Communal.INSTANCE, StatusBarState.KEYGUARD, - SceneKey.Shade.INSTANCE, StatusBarState.SHADE_LOCKED, - SceneKey.QuickSettings.INSTANCE, StatusBarState.SHADE_LOCKED + Scenes.Lockscreen, StatusBarState.KEYGUARD, + Scenes.Bouncer, StatusBarState.KEYGUARD, + Scenes.Communal, StatusBarState.KEYGUARD, + Scenes.Shade, StatusBarState.SHADE_LOCKED, + Scenes.QuickSettings, StatusBarState.SHADE_LOCKED ); /** 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 8d5302231fc1..5f3a83aa35e0 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 @@ -1761,7 +1761,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * Constructs an ExpandableNotificationRow. Used by layout inflation. * * @param context passed to image resolver - * @param attrs attributes used to initialize parent view + * @param attrs attributes used to initialize parent view */ public ExpandableNotificationRow(Context context, AttributeSet attrs) { this(context, attrs, context); @@ -1775,9 +1775,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * AsyncLayoutFactory} in {@link RowInflaterTask}. * * @param context context context of the view - * @param attrs attributes used to initialize parent view - * @param entry notification that the row will be associated to (determines the user for the - * ImageResolver) + * @param attrs attributes used to initialize parent view + * @param entry notification that the row will be associated to (determines the user for the + * ImageResolver) */ public ExpandableNotificationRow(Context context, AttributeSet attrs, NotificationEntry entry) { this(context, attrs, userContextForEntry(context, entry)); @@ -2028,7 +2028,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return traceTag; } - return traceTag + "(" + getEntry().getNotificationStyle() + ")"; + return traceTag + "(" + getEntry().getNotificationStyle() + ")"; } @Override @@ -3083,6 +3083,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView onStartedRunnable.run(); } } + @Override public void onAnimationEnd(Animator animation) { ExpandableNotificationRow.super.performRemoveAnimation( @@ -3777,6 +3778,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.print(", privateShowing: " + (showingLayout == mPrivateLayout)); pw.print(", mShowNoBackground: " + mShowNoBackground); pw.println(); + if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) { + dumpHeights(pw); + } showingLayout.dump(pw, args); if (getViewState() != null) { @@ -3820,6 +3824,34 @@ public class ExpandableNotificationRow extends ActivatableNotificationView }); } + private void dumpHeights(IndentingPrintWriter pw) { + pw.print("Heights: "); + pw.print("intrinsic", getIntrinsicHeight()); + pw.print("actual", getActualHeight()); + pw.print("maxContent", getMaxContentHeight()); + pw.print("maxExpanded", getMaxExpandHeight()); + pw.print("collapsed", getCollapsedHeight()); + pw.print("headsup", getHeadsUpHeight()); + pw.print("headsup without header", getHeadsUpHeightWithoutHeader()); + pw.print("minHeight", getMinHeight()); + pw.print("pinned headsup", getPinnedHeadsUpHeight( + true /* atLeastMinHeight */)); + pw.println(); + pw.print("Intrinsic Height Factors: "); + pw.print("isUserLocked()", isUserLocked()); + pw.print("isChildInGroup()", isChildInGroup()); + pw.print("isGroupExpanded()", isGroupExpanded()); + pw.print("sensitive", mSensitive); + pw.print("hideSensitiveForIntrinsicHeight", mHideSensitiveForIntrinsicHeight); + pw.print("isSummaryWithChildren", mIsSummaryWithChildren); + pw.print("canShowHeadsUp()", canShowHeadsUp()); + pw.print("isHeadsUpState()", isHeadsUpState()); + pw.print("isPinned()", isPinned()); + pw.print("headsupDisappearRunning", mHeadsupDisappearRunning); + pw.print("isExpanded()", isExpanded()); + pw.println(); + } + private void logKeepInParentChildDetached(ExpandableNotificationRow child) { if (mLogger != null) { mLogger.logKeepInParentChildDetached(child.getEntry(), getEntry()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 50bc3d31aa42..137e1b2ab809 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt; import com.android.systemui.statusbar.policy.SmartReplyView; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.Compile; +import com.android.systemui.util.DumpUtilsKt; import java.io.PrintWriter; import java.util.ArrayList; @@ -97,6 +98,8 @@ public class NotificationContentView extends FrameLayout implements Notification private static final int UNDEFINED = -1; + protected static final boolean INCLUDE_HEIGHTS_TO_DUMP = true; + private final Rect mClipBounds = new Rect(); private int mMinContractedHeight; @@ -2196,6 +2199,11 @@ public class NotificationContentView extends FrameLayout implements Notification pw.print("null"); } pw.println(); + + if (INCLUDE_HEIGHTS_TO_DUMP) { + dumpContentDimensions(DumpUtilsKt.asIndenting(pw)); + } + pw.println("mBubblesEnabledForUser: " + mBubblesEnabledForUser); pw.print("RemoteInputViews { "); @@ -2216,6 +2224,69 @@ public class NotificationContentView extends FrameLayout implements Notification pw.println(" }"); } + private String visibleTypeToString(int visibleType) { + return switch (visibleType) { + case VISIBLE_TYPE_CONTRACTED -> "CONTRACTED"; + case VISIBLE_TYPE_EXPANDED -> "EXPANDED"; + case VISIBLE_TYPE_HEADSUP -> "HEADSUP"; + case VISIBLE_TYPE_SINGLELINE -> "SINGLELINE"; + default -> "NONE"; + }; + } + + /** Add content views to dump */ + private void dumpContentDimensions(IndentingPrintWriter pw) { + pw.print("ContentDimensions: "); + pw.print("visibleType(String)", visibleTypeToString(mVisibleType)); + pw.print("measured width", getMeasuredWidth()); + pw.print("measured height", getMeasuredHeight()); + pw.print("maxHeight", getMaxHeight()); + pw.print("minHeight", getMinHeight()); + pw.println(); + pw.println("ChildViews:"); + DumpUtilsKt.withIncreasedIndent(pw, () -> { + final View contractedChild = mContractedChild; + if (contractedChild != null) { + dumpChildViewDimensions(pw, contractedChild, "Contracted Child:"); + pw.println(); + } + + final View expandedChild = mExpandedChild; + if (expandedChild != null) { + dumpChildViewDimensions(pw, expandedChild, "Expanded Child:"); + pw.println(); + } + + final View headsUpChild = mHeadsUpChild; + if (headsUpChild != null) { + dumpChildViewDimensions(pw, headsUpChild, "HeadsUp Child:"); + pw.println(); + } + final View singleLineView = mSingleLineView; + if (singleLineView != null) { + dumpChildViewDimensions(pw, singleLineView, "Single Line View:"); + pw.println(); + } + + }); + final int expandedRemoteInputHeight = getExtraRemoteInputHeight(mExpandedRemoteInput); + final int headsUpRemoteInputHeight = getExtraRemoteInputHeight(mHeadsUpRemoteInput); + pw.print("expandedRemoteInputHeight", expandedRemoteInputHeight); + pw.print("headsUpRemoteInputHeight", headsUpRemoteInputHeight); + pw.println(); + } + + private void dumpChildViewDimensions(IndentingPrintWriter pw, View view, + String name) { + pw.print(name + " "); + DumpUtilsKt.withIncreasedIndent(pw, () -> { + pw.print("width", view.getWidth()); + pw.print("height", view.getHeight()); + pw.print("measuredWidth", view.getMeasuredWidth()); + pw.print("measuredHeight", view.getMeasuredHeight()); + }); + } + /** Add any existing SmartReplyView to the dump */ public void dumpSmartReplies(IndentingPrintWriter pw) { if (mHeadsUpSmartReplyView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java index 9445d56ab509..ea3036e35c1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java @@ -31,6 +31,7 @@ import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import com.android.systemui.res.R; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.util.time.SystemClock; import javax.inject.Inject; @@ -46,9 +47,14 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf private NotificationEntry mEntry; private boolean mCancelled; private Throwable mInflateOrigin; + private final SystemClock mSystemClock; + private final RowInflaterTaskLogger mLogger; + private long mInflateStartTimeMs; @Inject - public RowInflaterTask() { + public RowInflaterTask(SystemClock systemClock, RowInflaterTaskLogger logger) { + mSystemClock = systemClock; + mLogger = logger; } /** @@ -61,29 +67,49 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf } mListener = listener; AsyncLayoutInflater inflater = com.android.systemui.Flags.notificationRowUserContext() - ? new AsyncLayoutInflater(context, new RowAsyncLayoutInflater(entry)) + ? new AsyncLayoutInflater(context, makeRowInflater(entry)) : new AsyncLayoutInflater(context); mEntry = entry; entry.setInflationTask(this); + + mLogger.logInflateStart(entry); + mInflateStartTimeMs = mSystemClock.elapsedRealtime(); inflater.inflate(R.layout.status_bar_notification_row, parent, this); } + private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) { + return new RowAsyncLayoutInflater(entry, mSystemClock, mLogger); + } + @VisibleForTesting static class RowAsyncLayoutInflater implements AsyncLayoutFactory { private final NotificationEntry mEntry; + private final SystemClock mSystemClock; + private final RowInflaterTaskLogger mLogger; - RowAsyncLayoutInflater(NotificationEntry entry) { + RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock, + RowInflaterTaskLogger logger) { mEntry = entry; + mSystemClock = systemClock; + mLogger = logger; } @Nullable @Override public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) { - if (name.equals(ExpandableNotificationRow.class.getName())) { - return new ExpandableNotificationRow(context, attrs, mEntry); + if (!name.equals(ExpandableNotificationRow.class.getName())) { + return null; } - return null; + + final long startMs = mSystemClock.elapsedRealtime(); + final ExpandableNotificationRow row = + new ExpandableNotificationRow(context, attrs, mEntry); + final long elapsedMs = mSystemClock.elapsedRealtime() - startMs; + + mLogger.logCreatedRow(mEntry, elapsedMs); + + return row; } @Nullable @@ -101,6 +127,9 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf @Override public void onInflateFinished(View view, int resid, ViewGroup parent) { + final long elapsedMs = mSystemClock.elapsedRealtime() - mInflateStartTimeMs; + mLogger.logInflateFinish(mEntry, elapsedMs, mCancelled); + if (!mCancelled) { try { mEntry.onInflationTaskFinished(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt new file mode 100644 index 000000000000..94da6bec799b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 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.row + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.dagger.NotifInflationLog +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.logKey +import javax.inject.Inject + +class RowInflaterTaskLogger @Inject constructor(@NotifInflationLog private val buffer: LogBuffer) { + fun logInflateStart(entry: NotificationEntry) { + buffer.log( + TAG, + LogLevel.DEBUG, + { str1 = entry.logKey }, + { "started row inflation for $str1" } + ) + } + + fun logCreatedRow(entry: NotificationEntry, elapsedMs: Long) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + long1 = elapsedMs + }, + { "created row in $long1 ms for $str1" } + ) + } + + fun logInflateFinish(entry: NotificationEntry, elapsedMs: Long, cancelled: Boolean) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + long1 = elapsedMs + bool1 = cancelled + }, + { "finished ${if (bool1) "cancelled " else ""}row inflation in $long1 ms for $str1" } + ) + } +} + +private const val TAG = "RowInflaterTask" 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 9c03405304ef..27db84f6715e 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 @@ -319,6 +319,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { + if (SceneContainerFlag.isEnabled() && !mChildrenUpdateRequested) { + getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } updateForcedScroll(); updateChildren(); mChildrenUpdateRequested = false; @@ -680,7 +684,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable res.getBoolean(R.bool.config_drawNotificationBackground); setOutlineProvider(mOutlineProvider); - boolean willDraw = mShouldDrawNotificationBackground || mDebugLines; + // We could set this whenever we 'requestChildUpdate' much like the viewTreeObserver, but + // that adds a bunch of complexity, and drawing nothing isn't *that* expensive. + boolean willDraw = SceneContainerFlag.isEnabled() + || mShouldDrawNotificationBackground || mDebugLines; setWillNotDraw(!willDraw); mBackgroundPaint.setAntiAlias(true); if (mDebugLines) { @@ -816,7 +823,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } + private void onJustBeforeDraw() { + if (SceneContainerFlag.isEnabled()) { + if (mChildrenUpdateRequested) { + updateForcedScroll(); + updateChildren(); + mChildrenUpdateRequested = false; + } + } + } + protected void onDraw(Canvas canvas) { + onJustBeforeDraw(); if (mShouldDrawNotificationBackground && (mSections[0].getCurrentBounds().top < mSections[mSections.length - 1].getCurrentBounds().bottom diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt new file mode 100644 index 000000000000..9cd46f5b3552 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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.stack.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.power.domain.interactor.PowerInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +/** Interactor exposing states related to the stack's context */ +@SysUISingleton +class NotificationStackInteractor +@Inject +constructor( + keyguardInteractor: KeyguardInteractor, + powerInteractor: PowerInteractor, +) { + val isShowingOnLockscreen: Flow<Boolean> = + combine( + // Non-notification UI elements of the notification list should not be visible + // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on + // top. See b/219680200 for the footer and b/228790482, b/267060171 for the + // empty shade. + // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState + // entirely, so this will have to be replaced at some point. + keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD }, + // The StatusBarState is unfortunately not updated quickly enough when the power + // button is pressed, so this is necessary in addition to the KEYGUARD check to + // cover the transition to AOD while going to sleep (b/190227875). + powerInteractor.isAsleep, + ) { (isOnKeyguard, isAsleep) -> + isOnKeyguard || isAsleep + } + .distinctUntilChanged() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt index 7b502564ecb1..c85a18a8a896 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.keyguard.shared.model.StatusBarState -import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor @@ -26,6 +23,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.SeenNotific import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import com.android.systemui.util.kotlin.combine @@ -51,8 +49,7 @@ constructor( val footer: Optional<FooterViewModel>, val logger: Optional<NotificationLoggerViewModel>, activeNotificationsInteractor: ActiveNotificationsInteractor, - keyguardInteractor: KeyguardInteractor, - powerInteractor: PowerInteractor, + notificationStackInteractor: NotificationStackInteractor, remoteInputInteractor: RemoteInputInteractor, seenNotificationsInteractor: SeenNotificationsInteractor, shadeInteractor: ShadeInteractor, @@ -71,7 +68,7 @@ constructor( } else { combine( activeNotificationsInteractor.areAnyNotificationsPresent, - isShowingOnLockscreen, + notificationStackInteractor.isShowingOnLockscreen, ) { hasNotifications, isShowingOnLockscreen -> hasNotifications || !isShowingOnLockscreen } @@ -86,7 +83,7 @@ constructor( combine( activeNotificationsInteractor.areAnyNotificationsPresent, shadeInteractor.isQsFullscreen, - isShowingOnLockscreen, + notificationStackInteractor.isShowingOnLockscreen, ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen -> when { hasNotifications -> false @@ -109,7 +106,7 @@ constructor( combine( activeNotificationsInteractor.areAnyNotificationsPresent, userSetupInteractor.isUserSetUp, - isShowingOnLockscreen, + notificationStackInteractor.isShowingOnLockscreen, shadeInteractor.qsExpansion, shadeInteractor.isQsFullscreen, remoteInputInteractor.isRemoteInputActive, @@ -177,29 +174,6 @@ constructor( SHOW_WITH_ANIMATION(visible = true, canAnimate = true) } - private val isShowingOnLockscreen: Flow<Boolean> by lazy { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - combine( - // Non-notification UI elements of the notification list should not be visible - // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on - // top. See b/219680200 for the footer and b/228790482, b/267060171 for the - // empty shade. - // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState - // entirely, so this will have to be replaced at some point. - keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD }, - // The StatusBarState is unfortunately not updated quickly enough when the power - // button is pressed, so this is necessary in addition to the KEYGUARD check to - // cover the transition to AOD while going to sleep (b/190227875). - powerInteractor.isAsleep, - ) { (isOnKeyguard, isAsleep) -> - isOnKeyguard || isAsleep - } - .distinctUntilChanged() - } - } - // TODO(b/308591475): This should be tracked separately by the empty shade. val areNotificationsHiddenInShade: Flow<Boolean> by lazy { if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt index f68141a4181c..8d1cdfac90c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt @@ -17,12 +17,12 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor import com.android.systemui.util.kotlin.FlowDumperImpl @@ -54,7 +54,7 @@ constructor( ) { shadeExpansion, transitionState -> when (transitionState) { is ObservableTransitionState.Idle -> { - if (transitionState.scene == SceneKey.Lockscreen) { + if (transitionState.scene == Scenes.Lockscreen) { 1f } else { shadeExpansion @@ -62,10 +62,10 @@ constructor( } is ObservableTransitionState.Transition -> { if ( - (transitionState.fromScene == SceneKey.Shade && - transitionState.toScene == SceneKey.QuickSettings) || - (transitionState.fromScene == SceneKey.QuickSettings && - transitionState.toScene == SceneKey.Shade) + (transitionState.fromScene == Scenes.Shade && + transitionState.toScene == Scenes.QuickSettings) || + (transitionState.fromScene == Scenes.QuickSettings && + transitionState.toScene == Scenes.Shade) ) { 1f } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 3669ba851005..560d5ba32fd6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -309,7 +309,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba if (mVibrateOnOpening) { vibrateOnNavigationKeyDown(); } - mShadeViewController.expand(true /* animate */); + mShadeController.animateExpandShade(); mNotificationStackScrollLayoutController.setWillExpand(true); mHeadsUpManager.unpinAll(true /* userUnpinned */); mMetricsLogger.count("panel_open", 1); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 68a0e9cc1bf8..f29ec8f38c27 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -91,7 +91,7 @@ private constructor( if (event.source == InputDevice.SOURCE_MOUSE) { if (event.action == MotionEvent.ACTION_UP) { v.performClick() - shadeViewController.expand(/* animate= */ true) + shadeController.animateExpandShade() } return true } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index d1055c77ab8f..ee844345d2ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -81,6 +81,9 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.scene.domain.interactor.SceneInteractor; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; +import com.android.systemui.scene.shared.model.Scenes; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.ShadeExpansionListener; @@ -157,6 +160,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final BouncerView mPrimaryBouncerView; private final Lazy<ShadeController> mShadeController; + private final Lazy<SceneInteractor> mSceneInteractorLazy; // Local cache of expansion events, to avoid duplicates private float mFraction = -1f; @@ -381,7 +385,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy, SelectedUserInteractor selectedUserInteractor, Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor, - JavaAdapter javaAdapter + JavaAdapter javaAdapter, + Lazy<SceneInteractor> sceneInteractorLazy ) { mContext = context; mViewMediatorCallback = callback; @@ -415,6 +420,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mSelectedUserInteractor = selectedUserInteractor; mSurfaceBehindInteractor = surfaceBehindInteractor; mJavaAdapter = javaAdapter; + mSceneInteractorLazy = sceneInteractorLazy; } KeyguardTransitionInteractor mKeyguardTransitionInteractor; @@ -633,8 +639,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void show(Bundle options) { Trace.beginSection("StatusBarKeyguardViewManager#show"); mNotificationShadeWindowController.setKeyguardShowing(true); - mKeyguardStateController.notifyKeyguardState(true, - mKeyguardStateController.isOccluded()); + if (SceneContainerFlag.isEnabled()) { + mSceneInteractorLazy.get().changeScene( + Scenes.Lockscreen, "StatusBarKeyguardViewManager.show"); + } + mKeyguardStateController.notifyKeyguardState(true, mKeyguardStateController.isOccluded()); reset(true /* hideBouncerWhenShowing */); SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt index d19a3364d502..5c53ff98b777 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt @@ -16,8 +16,6 @@ package com.android.systemui.unfold -import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.annotation.BinderThread import android.content.Context @@ -25,6 +23,7 @@ import android.os.Handler import android.os.SystemProperties import android.util.Log import android.view.animation.DecelerateInterpolator +import androidx.core.animation.addListener import com.android.internal.foldables.FoldLockSettingAvailabilityProvider import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DeviceStateRepository @@ -37,25 +36,17 @@ import com.android.systemui.unfold.FullscreenLightRevealAnimationController.Comp import com.android.systemui.unfold.dagger.UnfoldBg import com.android.systemui.util.animation.data.repository.AnimationStatusRepository import javax.inject.Inject -import kotlin.coroutines.resume import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.android.asCoroutineDispatcher -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.launch -import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout -@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class FoldLightRevealOverlayAnimation @Inject constructor( @@ -70,9 +61,6 @@ constructor( private val revealProgressValueAnimator: ValueAnimator = ValueAnimator.ofFloat(ALPHA_OPAQUE, ALPHA_TRANSPARENT) - private val areAnimationEnabled: Flow<Boolean> - get() = animationStatusRepository.areAnimationsEnabled() - private lateinit var controller: FullscreenLightRevealAnimationController @Volatile private var readyCallback: CompletableDeferred<Runnable>? = null @@ -101,30 +89,33 @@ constructor( applicationScope.launch(bgHandler.asCoroutineDispatcher()) { deviceStateRepository.state - .map { it == DeviceStateRepository.DeviceState.FOLDED } + .map { it != DeviceStateRepository.DeviceState.FOLDED } .distinctUntilChanged() - .flatMapLatest { isFolded -> - flow<Nothing> { - if (!areAnimationEnabled.first() || !isFolded) { - return@flow - } - withTimeout(WAIT_FOR_ANIMATION_TIMEOUT_MS) { - readyCallback = CompletableDeferred() - val onReady = readyCallback?.await() - readyCallback = null - controller.addOverlay(ALPHA_OPAQUE, onReady) - waitForScreenTurnedOn() - } - playFoldLightRevealOverlayAnimation() - } - .catchTimeoutAndLog() - .onCompletion { - val onReady = readyCallback?.takeIf { it.isCompleted }?.getCompleted() - onReady?.run() + .filter { isUnfolded -> isUnfolded } + .collect { controller.ensureOverlayRemoved() } + } + + applicationScope.launch(bgHandler.asCoroutineDispatcher()) { + deviceStateRepository.state + .filter { + animationStatusRepository.areAnimationsEnabled().first() && + it == DeviceStateRepository.DeviceState.FOLDED + } + .collect { + try { + withTimeout(WAIT_FOR_ANIMATION_TIMEOUT_MS) { + readyCallback = CompletableDeferred() + val onReady = readyCallback?.await() readyCallback = null + controller.addOverlay(ALPHA_OPAQUE, onReady) + waitForScreenTurnedOn() + playFoldLightRevealOverlayAnimation() } + } catch (e: TimeoutCancellationException) { + Log.e(TAG, "Fold light reveal animation timed out") + ensureOverlayRemovedInternal() + } } - .collect {} } } @@ -137,34 +128,19 @@ constructor( powerInteractor.screenPowerState.filter { it == ScreenPowerState.SCREEN_ON }.first() } - private suspend fun playFoldLightRevealOverlayAnimation() { + private fun ensureOverlayRemovedInternal() { + revealProgressValueAnimator.cancel() + controller.ensureOverlayRemoved() + } + + private fun playFoldLightRevealOverlayAnimation() { revealProgressValueAnimator.duration = ANIMATION_DURATION revealProgressValueAnimator.interpolator = DecelerateInterpolator() revealProgressValueAnimator.addUpdateListener { animation -> controller.updateRevealAmount(animation.animatedFraction) } - revealProgressValueAnimator.startAndAwaitCompletion() - } - - private suspend fun ValueAnimator.startAndAwaitCompletion(): Unit = - suspendCancellableCoroutine { continuation -> - val listener = - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - continuation.resume(Unit) - removeListener(this) - } - } - addListener(listener) - continuation.invokeOnCancellation { removeListener(listener) } - start() - } - - private fun <T> Flow<T>.catchTimeoutAndLog() = catch { exception -> - when (exception) { - is TimeoutCancellationException -> Log.e(TAG, "Fold light reveal animation timed out") - else -> throw exception - } + revealProgressValueAnimator.addListener(onEnd = { controller.ensureOverlayRemoved() }) + revealProgressValueAnimator.start() } private companion object { diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt new file mode 100644 index 000000000000..0128eb762296 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.statusbar.policy.BatteryController +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onStart + +fun BatteryController.isBatteryPowerSaveEnabled(): Flow<Boolean> { + return conflatedCallbackFlow { + val batteryCallback = + object : BatteryController.BatteryStateChangeCallback { + override fun onPowerSaveChanged(isPowerSave: Boolean) { + trySend(isPowerSave) + } + } + addCallback(batteryCallback) + awaitClose { removeCallback(batteryCallback) } + } + .onStart { emit(isPowerSave) } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt new file mode 100644 index 000000000000..22cc8dd7745d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.statusbar.policy.RotationLockController +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onStart + +fun RotationLockController.isRotationLockEnabled(): Flow<Boolean> { + return conflatedCallbackFlow { + val rotationLockCallback = + RotationLockController.RotationLockControllerCallback { rotationLocked, _ -> + trySend(rotationLocked) + } + addCallback(rotationLockCallback) + awaitClose { removeCallback(rotationLockCallback) } + } + .onStart { emit(isRotationLocked) } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt index 170b32c1d0ea..2ab59984d06f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt @@ -16,14 +16,11 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor -import android.content.Intent -import android.provider.Settings import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.animation.Expandable import com.android.systemui.media.dialog.MediaOutputDialogFactory -import com.android.systemui.plugins.ActivityStarter import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import javax.inject.Inject @@ -34,17 +31,8 @@ class MediaOutputActionsInteractor @Inject constructor( private val mediaOutputDialogFactory: MediaOutputDialogFactory, - private val activityStarter: ActivityStarter, ) { - fun onDeviceClick(expandable: Expandable) { - activityStarter.startActivity( - Intent(Settings.ACTION_BLUETOOTH_SETTINGS), - true, - expandable.activityTransitionController(), - ) - } - fun onBarClick(session: MediaDeviceSession, expandable: Expandable) { when (session) { is MediaDeviceSession.Active -> { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt index e518ed022792..37bf661454c5 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt @@ -26,13 +26,13 @@ sealed interface DeviceIconViewModel { val iconColor: Color val backgroundColor: Color - class IsPlaying( + data class IsPlaying( override val icon: Icon, override val iconColor: Color, override val backgroundColor: Color, ) : DeviceIconViewModel - class IsNotPlaying( + data class IsNotPlaying( override val icon: Icon, override val iconColor: Color, override val backgroundColor: Color, diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt index 85d6c9e341ac..37661b53c98a 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt @@ -113,11 +113,6 @@ constructor( private fun MediaDeviceSession.isPlaying(): Boolean = this is MediaDeviceSession.Active && playbackState?.isActive == true - fun onDeviceClick(expandable: Expandable) { - actionsInteractor.onDeviceClick(expandable) - volumePanelViewModel.dismissPanel() - } - fun onBarClick(expandable: Expandable) { actionsInteractor.onBarClick(mediaDeviceSession.value, expandable) volumePanelViewModel.dismissPanel() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java index 90587d7386ce..13fb42ce8c3e 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,6 +32,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.power.data.repository.FakePowerRepository; import com.android.systemui.power.domain.interactor.PowerInteractorFactory; import com.android.systemui.res.R; @@ -59,6 +62,7 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardStatusViewController mControllerMock; @Mock protected InteractionJankMonitor mInteractionJankMonitor; @Mock protected ViewTreeObserver mViewTreeObserver; + @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Mock protected DumpManager mDumpManager; protected FakeKeyguardRepository mFakeKeyguardRepository; protected FakePowerRepository mFakePowerRepository; @@ -89,6 +93,7 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { mKeyguardLogger, mInteractionJankMonitor, deps.getKeyguardInteractor(), + mKeyguardTransitionInteractor, mDumpManager, PowerInteractorFactory.create( mFakePowerRepository @@ -105,6 +110,7 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver); when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch); + when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow()); when(mKeyguardStatusView.findViewById(R.id.keyguard_status_area)) .thenReturn(mKeyguardStatusAreaView); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt index d0b1dd52c7e7..df52265384fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt @@ -122,13 +122,7 @@ class BurnInInteractorTest : SysuiTestCase() { testScope.runTest { whenever(burnInHelperWrapper.burnInScale()).thenReturn(0.5f) - val burnInModel by - collectLastValue( - underTest.burnIn( - xDimenResourceId = R.dimen.burn_in_prevention_offset_x, - yDimenResourceId = R.dimen.burn_in_prevention_offset_y - ) - ) + val burnInModel by collectLastValue(underTest.keyguardBurnIn) // After time tick, returns the configured values fakeKeyguardRepository.dozeTimeTick(10) 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 index 72a890d6a5e5..c65a9ef28bba 100644 --- 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 @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN import com.android.systemui.Flags.FLAG_COMMUNAL_HUB @@ -25,8 +26,7 @@ import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.communal.domain.interactor.communalInteractor -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeCommandQueue @@ -654,6 +654,30 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { coroutineContext.cancelChildren() } + @Test + fun dozingToPrimaryBouncer() = + testScope.runTest { + // GIVEN a prior transition has run to DOZING + runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING) + runCurrent() + + // WHEN awaked by a request to show the primary bouncer, as can happen if SPFS is + // touched after boot + powerInteractor.setAwakeForTest() + bouncerRepository.setPrimaryShow(true) + advanceTimeBy(60L) + + assertThat(transitionRepository) + .startedTransition( + to = KeyguardState.PRIMARY_BOUNCER, + from = KeyguardState.DOZING, + ownerName = "FromDozingTransitionInteractor", + animatorAssertion = { it.isNotNull() } + ) + + coroutineContext.cancelChildren() + } + /** This handles security method NONE and screen off with lock timeout */ @Test fun dozingToGoneWithKeyguardNotShowing() = @@ -688,8 +712,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -846,8 +870,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -995,8 +1019,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1103,8 +1127,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1138,8 +1162,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1256,8 +1280,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1445,13 +1469,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // WHEN a transition to the glanceable hub starts - val currentScene = CommunalSceneKey.Blank - val targetScene = CommunalSceneKey.Communal + val currentScene = CommunalScenes.Blank + val targetScene = CommunalScenes.Communal val progress = MutableStateFlow(0f) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Transition( + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( fromScene = currentScene, toScene = targetScene, progress = progress, @@ -1624,13 +1648,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // WHEN a glanceable hub transition starts - val currentScene = CommunalSceneKey.Blank - val targetScene = CommunalSceneKey.Communal + val currentScene = CommunalScenes.Blank + val targetScene = CommunalScenes.Communal val progress = MutableStateFlow(0f) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Transition( + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( fromScene = currentScene, toScene = targetScene, progress = progress, @@ -1655,8 +1679,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { clearInvocations(transitionRepository) runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB) val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(currentScene) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(currentScene) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1680,13 +1704,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // WHEN a transition away from glanceable hub starts - val currentScene = CommunalSceneKey.Communal - val targetScene = CommunalSceneKey.Blank + val currentScene = CommunalScenes.Communal + val targetScene = CommunalScenes.Blank val progress = MutableStateFlow(0f) val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Transition( + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( fromScene = currentScene, toScene = targetScene, progress = progress, @@ -1710,8 +1734,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { clearInvocations(transitionRepository) runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.LOCKSCREEN) val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(currentScene) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(currentScene) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1797,8 +1821,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the device is idle on the glanceable hub val idleTransitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) ) communalInteractor.setTransitionState(idleTransitionState) runCurrent() @@ -1854,12 +1878,12 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // WHEN a transition away from glanceable hub starts - val currentScene = CommunalSceneKey.Communal - val targetScene = CommunalSceneKey.Blank + val currentScene = CommunalScenes.Communal + val targetScene = CommunalScenes.Blank val transitionState = - MutableStateFlow<ObservableCommunalTransitionState>( - ObservableCommunalTransitionState.Transition( + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( fromScene = currentScene, toScene = targetScene, progress = flowOf(0f, 0.1f), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt index 02bd810a43d5..4bb0d4781376 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt @@ -27,11 +27,14 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel.Companion.UNLOCKED_DELAY_MS import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.runner.RunWith @@ -71,7 +74,10 @@ class DeviceEntryIconViewModelTest : SysuiTestCase() { fun isLongPressEnabled_unlocked() = testScope.runTest { val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled) + fingerprintPropertyRepository.supportsUdfps() keyguardRepository.setKeyguardDismissible(true) + advanceTimeBy(UNLOCKED_DELAY_MS * 2) // wait for unlocked delay + runCurrent() assertThat(isLongPressEnabled).isTrue() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt index ce089b1b723a..864acfb1ddb9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase @@ -25,13 +24,9 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.doze.util.BurnInHelperWrapper import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory -import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition -import com.android.systemui.kosmos.testScope -import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -41,23 +36,18 @@ 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.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidJUnit4::class) +@RunWith(JUnit4::class) class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { - private val kosmos = testKosmos() - private val testScope = kosmos.testScope @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper @Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel - @Mock private lateinit var burnInInteractor: BurnInInteractor - private val burnInFlow = MutableStateFlow(BurnInModel()) - - private lateinit var bottomAreaInteractor: KeyguardBottomAreaInteractor private lateinit var underTest: KeyguardIndicationAreaViewModel private lateinit var repository: FakeKeyguardRepository @@ -80,11 +70,9 @@ class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) whenever(burnInHelperWrapper.burnInOffset(anyInt(), any())) .thenReturn(RETURNED_BURN_IN_OFFSET) - whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow) val withDeps = KeyguardInteractorFactory.create() val keyguardInteractor = withDeps.keyguardInteractor @@ -94,85 +82,78 @@ class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow) whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow) whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow) - bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository) underTest = KeyguardIndicationAreaViewModel( keyguardInteractor = keyguardInteractor, - bottomAreaInteractor = bottomAreaInteractor, + bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository), keyguardBottomAreaViewModel = bottomAreaViewModel, burnInHelperWrapper = burnInHelperWrapper, - burnInInteractor = burnInInteractor, shortcutsCombinedViewModel = shortcutsCombinedViewModel, configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()), ) } @Test - fun alpha() = - testScope.runTest { - val value = collectLastValue(underTest.alpha) - - assertThat(value()).isEqualTo(1f) - alphaFlow.value = 0.1f - assertThat(value()).isEqualTo(0.1f) - alphaFlow.value = 0.5f - assertThat(value()).isEqualTo(0.5f) - alphaFlow.value = 0.2f - assertThat(value()).isEqualTo(0.2f) - alphaFlow.value = 0f - assertThat(value()).isEqualTo(0f) - } + fun alpha() = runTest { + val value = collectLastValue(underTest.alpha) + + assertThat(value()).isEqualTo(1f) + alphaFlow.value = 0.1f + assertThat(value()).isEqualTo(0.1f) + alphaFlow.value = 0.5f + assertThat(value()).isEqualTo(0.5f) + alphaFlow.value = 0.2f + assertThat(value()).isEqualTo(0.2f) + alphaFlow.value = 0f + assertThat(value()).isEqualTo(0f) + } @Test - fun isIndicationAreaPadded() = - testScope.runTest { - repository.setKeyguardShowing(true) - val value = collectLastValue(underTest.isIndicationAreaPadded) - - assertThat(value()).isFalse() - startButtonFlow.value = startButtonFlow.value.copy(isVisible = true) - assertThat(value()).isTrue() - endButtonFlow.value = endButtonFlow.value.copy(isVisible = true) - assertThat(value()).isTrue() - startButtonFlow.value = startButtonFlow.value.copy(isVisible = false) - assertThat(value()).isTrue() - endButtonFlow.value = endButtonFlow.value.copy(isVisible = false) - assertThat(value()).isFalse() - } + fun isIndicationAreaPadded() = runTest { + repository.setKeyguardShowing(true) + val value = collectLastValue(underTest.isIndicationAreaPadded) + + assertThat(value()).isFalse() + startButtonFlow.value = startButtonFlow.value.copy(isVisible = true) + assertThat(value()).isTrue() + endButtonFlow.value = endButtonFlow.value.copy(isVisible = true) + assertThat(value()).isTrue() + startButtonFlow.value = startButtonFlow.value.copy(isVisible = false) + assertThat(value()).isTrue() + endButtonFlow.value = endButtonFlow.value.copy(isVisible = false) + assertThat(value()).isFalse() + } @Test - fun indicationAreaTranslationX() = - testScope.runTest { - val value = collectLastValue(underTest.indicationAreaTranslationX) - - assertThat(value()).isEqualTo(0f) - bottomAreaInteractor.setClockPosition(100, 100) - assertThat(value()).isEqualTo(100f) - bottomAreaInteractor.setClockPosition(200, 100) - assertThat(value()).isEqualTo(200f) - bottomAreaInteractor.setClockPosition(200, 200) - assertThat(value()).isEqualTo(200f) - bottomAreaInteractor.setClockPosition(300, 100) - assertThat(value()).isEqualTo(300f) - } + fun indicationAreaTranslationX() = runTest { + val value = collectLastValue(underTest.indicationAreaTranslationX) + + assertThat(value()).isEqualTo(0f) + repository.setClockPosition(100, 100) + assertThat(value()).isEqualTo(100f) + repository.setClockPosition(200, 100) + assertThat(value()).isEqualTo(200f) + repository.setClockPosition(200, 200) + assertThat(value()).isEqualTo(200f) + repository.setClockPosition(300, 100) + assertThat(value()).isEqualTo(300f) + } @Test - fun indicationAreaTranslationY() = - testScope.runTest { - val value = - collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET)) - - // Negative 0 - apparently there's a difference in floating point arithmetic - FML - assertThat(value()).isEqualTo(-0f) - val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f) - assertThat(value()).isEqualTo(expected1) - val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f) - assertThat(value()).isEqualTo(expected2) - val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f) - assertThat(value()).isEqualTo(expected3) - val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f) - assertThat(value()).isEqualTo(expected4) - } + fun indicationAreaTranslationY() = runTest { + val value = collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET)) + + // Negative 0 - apparently there's a difference in floating point arithmetic - FML + assertThat(value()).isEqualTo(-0f) + val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f) + assertThat(value()).isEqualTo(expected1) + val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f) + assertThat(value()).isEqualTo(expected2) + val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f) + assertThat(value()).isEqualTo(expected3) + val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f) + assertThat(value()).isEqualTo(expected4) + } private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float { repository.setDozeAmount(dozeAmount) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt index 45f49f01a43e..29820f7a7249 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt @@ -28,7 +28,7 @@ import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq import com.android.systemui.dreams.DreamOverlayStateController import com.android.systemui.keyguard.WakefulnessLifecycle @@ -514,7 +514,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { kosmos.setCommunalAvailable(true) runCurrent() - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( @@ -526,7 +526,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ) clearInvocations(mediaCarouselController) - communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + communalInteractor.onSceneChanged(CommunalScenes.Blank) runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( @@ -549,7 +549,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) // UMO goes to communal even over the lock screen. - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( @@ -571,7 +571,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { // Device is on lock screen. whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + communalInteractor.onSceneChanged(CommunalScenes.Communal) runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt index 2f911fffe335..92c240404b24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt @@ -22,8 +22,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import java.lang.IllegalStateException +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -31,12 +33,14 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify @SmallTest +@OptIn(ExperimentalCoroutinesApi::class) class ScreenshotSoundControllerTest : SysuiTestCase() { private val soundProvider = mock<ScreenshotSoundProvider>() private val mediaPlayer = mock<MediaPlayer>() private val bgDispatcher = UnconfinedTestDispatcher() private val scope = TestScope(bgDispatcher) + @Before fun setup() { whenever(soundProvider.getScreenshotSound()).thenReturn(mediaPlayer) @@ -45,52 +49,59 @@ class ScreenshotSoundControllerTest : SysuiTestCase() { @Test fun init_soundLoading() { createController() - bgDispatcher.scheduler.runCurrent() + scope.advanceUntilIdle() verify(soundProvider).getScreenshotSound() } @Test - fun init_soundLoadingException_playAndReleaseDoNotThrow() = runTest { - whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException()) + fun init_soundLoadingException_playAndReleaseDoNotThrow() = + scope.runTest { + whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException()) - val controller = createController() + val controller = createController() - controller.playCameraSound().await() - controller.releaseScreenshotSound().await() + controller.playScreenshotSound() + advanceUntilIdle() - verify(mediaPlayer, never()).start() - verify(mediaPlayer, never()).release() - } + verify(mediaPlayer, never()).start() + verify(mediaPlayer, never()).release() + } @Test - fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = runTest { - val controller = createController() + fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = + scope.runTest { + val controller = createController() - controller.playCameraSound().await() + controller.playScreenshotSound() + advanceUntilIdle() - verify(mediaPlayer).start() - } + verify(mediaPlayer).start() + } @Test - fun playCameraSound_illegalStateException_doesNotThrow() = runTest { - whenever(mediaPlayer.start()).thenThrow(IllegalStateException()) + fun playCameraSound_illegalStateException_doesNotThrow() = + scope.runTest { + whenever(mediaPlayer.start()).thenThrow(IllegalStateException()) - val controller = createController() - controller.playCameraSound().await() + val controller = createController() + controller.playScreenshotSound() + advanceUntilIdle() - verify(mediaPlayer).start() - verify(mediaPlayer).release() - } + verify(mediaPlayer).start() + verify(mediaPlayer).release() + } @Test - fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest { - val controller = createController() + fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = + scope.runTest { + val controller = createController() - controller.releaseScreenshotSound().await() + controller.releaseScreenshotSound() + advanceUntilIdle() - verify(mediaPlayer).release() - } + verify(mediaPlayer).release() + } private fun createController() = ScreenshotSoundControllerImpl(soundProvider, scope, bgDispatcher) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 96574e245d2d..62d2d0efe24c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -26,6 +26,7 @@ import android.view.View import android.view.WindowManager import android.widget.FrameLayout import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalRepository @@ -33,7 +34,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable -import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos @@ -152,7 +153,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_communalClosed_doesNotIntercept() { // Communal is closed. - goToScene(CommunalSceneKey.Blank) + goToScene(CommunalScenes.Blank) assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() } @@ -160,7 +161,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_openGesture_interceptsTouches() { // Communal is closed. - goToScene(CommunalSceneKey.Blank) + goToScene(CommunalScenes.Blank) // Initial touch down is intercepted, and so are touches outside of the region, until an // up event is received. @@ -173,7 +174,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_communalOpen_interceptsTouches() { // Communal is open. - goToScene(CommunalSceneKey.Communal) + goToScene(CommunalScenes.Communal) // Touch events are intercepted outside of any gesture areas. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() @@ -184,7 +185,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_topSwipeWhenCommunalOpen_doesNotIntercept() { // Communal is open. - goToScene(CommunalSceneKey.Communal) + goToScene(CommunalScenes.Communal) // Touch event in the top swipe reqgion is not intercepted. assertThat(underTest.onTouchEvent(DOWN_IN_TOP_SWIPE_REGION_EVENT)).isFalse() @@ -193,7 +194,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_bottomSwipeWhenCommunalOpen_doesNotIntercept() { // Communal is open. - goToScene(CommunalSceneKey.Communal) + goToScene(CommunalScenes.Communal) // Touch event in the bottom swipe reqgion is not intercepted. assertThat(underTest.onTouchEvent(DOWN_IN_BOTTOM_SWIPE_REGION_EVENT)).isFalse() @@ -202,7 +203,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() { // Communal is open. - goToScene(CommunalSceneKey.Communal) + goToScene(CommunalScenes.Communal) // Bouncer is visible. bouncerShowingFlow.value = true @@ -217,7 +218,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() { // Communal is open. - goToScene(CommunalSceneKey.Communal) + goToScene(CommunalScenes.Communal) shadeShowingFlow.value = true testableLooper.processAllMessages() @@ -229,7 +230,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Test fun onTouchEvent_containerViewDisposed_doesNotIntercept() { // Communal is open. - goToScene(CommunalSceneKey.Communal) + goToScene(CommunalScenes.Communal) // Touch events are intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() @@ -262,7 +263,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { wm.updateViewLayout(parentView, lp) } - private fun goToScene(scene: CommunalSceneKey) { + private fun goToScene(scene: SceneKey) { communalRepository.setDesiredScene(scene) testableLooper.processAllMessages() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index f8771b26476c..fd7b1399d03f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -461,6 +461,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mKeyguardLogger, mInteractionJankMonitor, mKeyguardInteractor, + mKeyguardTransitionInteractor, mDumpManager, mPowerInteractor)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt index 651006dfc953..2f957b09467b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt @@ -5,6 +5,8 @@ package com.android.systemui.shade.transition import android.platform.test.annotations.DisableFlags import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -20,8 +22,7 @@ import com.android.systemui.scene.domain.interactor.PanelExpansionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.FakeSceneDataSource -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.STATE_OPENING import com.android.systemui.shade.ShadeExpansionChangeEvent @@ -117,13 +118,13 @@ class ShadeTransitionControllerTest : SysuiTestCase() { setUnlocked(true) val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Gone) + ObservableTransitionState.Idle(Scenes.Gone) ) sceneInteractor.setTransitionState(transitionState) - changeScene(SceneKey.Gone, transitionState) + changeScene(Scenes.Gone, transitionState) val currentScene by collectLastValue(sceneInteractor.currentScene) - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(latestChangeEvent) .isEqualTo( @@ -135,7 +136,7 @@ class ShadeTransitionControllerTest : SysuiTestCase() { ) ) - changeScene(SceneKey.Shade, transitionState) { progress -> + changeScene(Scenes.Shade, transitionState) { progress -> assertThat(latestChangeEvent) .isEqualTo( ShadeExpansionChangeEvent( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index fe16347fa298..dfbb6ea08f82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -54,7 +54,6 @@ import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -87,6 +86,7 @@ import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever +import com.android.systemui.scene.shared.model.Scenes import org.mockito.MockitoAnnotations @SmallTest @@ -313,48 +313,48 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { kosmos.fakeDeviceEntryRepository.setUnlocked(false) runCurrent() kosmos.sceneInteractor.changeScene( - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, loggingReason = "reason" ) runCurrent() assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isFalse() - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) // Call start to begin hydrating based on the scene framework: underTest.start() - kosmos.sceneInteractor.changeScene(toScene = SceneKey.Bouncer, loggingReason = "reason") + kosmos.sceneInteractor.changeScene(toScene = Scenes.Bouncer, loggingReason = "reason") runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD) - kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason") + kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason") runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Shade) + assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED) kosmos.sceneInteractor.changeScene( - toScene = SceneKey.QuickSettings, + toScene = Scenes.QuickSettings, loggingReason = "reason" ) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.QuickSettings) + assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED) kosmos.sceneInteractor.changeScene( - toScene = SceneKey.Communal, + toScene = Scenes.Communal, loggingReason = "reason" ) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Communal) + assertThat(currentScene).isEqualTo(Scenes.Communal) assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD) kosmos.sceneInteractor.changeScene( - toScene = SceneKey.Lockscreen, + toScene = Scenes.Lockscreen, loggingReason = "reason" ) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD) } @@ -377,25 +377,25 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { ) kosmos.fakeDeviceEntryRepository.setUnlocked(true) runCurrent() - kosmos.sceneInteractor.changeScene(toScene = SceneKey.Gone, loggingReason = "reason") + kosmos.sceneInteractor.changeScene(toScene = Scenes.Gone, loggingReason = "reason") runCurrent() assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isTrue() - assertThat(currentScene).isEqualTo(SceneKey.Gone) + assertThat(currentScene).isEqualTo(Scenes.Gone) // Call start to begin hydrating based on the scene framework: underTest.start() - kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason") + kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason") runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.Shade) + assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(statusBarState).isEqualTo(StatusBarState.SHADE) kosmos.sceneInteractor.changeScene( - toScene = SceneKey.QuickSettings, + toScene = Scenes.QuickSettings, loggingReason = "reason" ) runCurrent() - assertThat(currentScene).isEqualTo(SceneKey.QuickSettings) + assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(statusBarState).isEqualTo(StatusBarState.SHADE) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index dbf7b6ce145e..012ff2e31562 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -38,6 +38,7 @@ import android.util.ArraySet import android.view.View import android.view.accessibility.accessibilityManager import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger import com.android.internal.logging.metricsLogger @@ -55,8 +56,7 @@ import com.android.systemui.scene.data.repository.WindowRootViewVisibilityReposi import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.UserContextProvider import com.android.systemui.shade.shadeControllerSceneImpl import com.android.systemui.statusbar.NotificationEntryHelper @@ -602,9 +602,9 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { private fun setIsLockscreenOrShadeVisible(isVisible: Boolean) { val key = if (isVisible) { - SceneKey.Lockscreen + Scenes.Lockscreen } else { - SceneKey.Bouncer + Scenes.Bouncer } sceneInteractor.changeScene(key, "test") sceneInteractor.setTransitionState( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index e78081fc34bd..fb49499fc29d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -89,6 +89,8 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; +import com.android.systemui.util.time.SystemClock; +import com.android.systemui.util.time.SystemClockImpl; import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesTestActivity; @@ -136,6 +138,8 @@ public class NotificationTestHelper { public final Runnable mFutureDismissalRunnable; private @InflationFlag int mDefaultInflationFlags; private final FakeFeatureFlags mFeatureFlags; + private final SystemClock mSystemClock; + private final RowInflaterTaskLogger mRowInflaterTaskLogger; public NotificationTestHelper( Context context, @@ -199,6 +203,9 @@ public class NotificationTestHelper { mFutureDismissalRunnable = mock(Runnable.class); when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt())) .thenReturn(mFutureDismissalRunnable); + + mSystemClock = new SystemClockImpl(); + mRowInflaterTaskLogger = mock(RowInflaterTaskLogger.class); } public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) { @@ -572,7 +579,8 @@ public class NotificationTestHelper { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( Context.LAYOUT_INFLATER_SERVICE); if (com.android.systemui.Flags.notificationRowUserContext()) { - inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry)); + inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry, mSystemClock, + mRowInflaterTaskLogger)); } mRow = (ExpandableNotificationRow) inflater.inflate( R.layout.status_bar_notification_row, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt new file mode 100644 index 000000000000..1c6bda985a0e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.stack.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.data.repository.fakePowerRepository +import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class NotificationStackInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + val underTest + get() = kosmos.notificationStackInteractor + + @Test + fun testIsShowingOnLockscreen_falseWhenViewingShade() = + kosmos.testScope.runTest { + val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen) + + // WHEN shade is open + kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE) + runCurrent() + + // THEN notifications are not showing on lockscreen + assertThat(onLockscreen).isFalse() + } + + @Test + fun testIsShowingOnLockscreen_trueWhenViewingKeyguard() = + kosmos.testScope.runTest { + val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen) + + // WHEN on keyguard + kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) + runCurrent() + + // THEN notifications are showing on lockscreen + assertThat(onLockscreen).isTrue() + } + + @Test + fun testIsShowingOnLockscreen_trueWhenStartingToSleep() = + kosmos.testScope.runTest { + val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen) + + // WHEN shade is open + kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE) + // AND device is starting to go to sleep + kosmos.fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP) + runCurrent() + + // THEN notifications are showing on lockscreen + assertThat(onLockscreen).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 3a94295de668..84156ee1fd53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -73,6 +73,7 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; +import com.android.compose.animation.scene.ObservableTransitionState; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -95,8 +96,7 @@ import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.communal.data.repository.CommunalRepository; import com.android.systemui.communal.domain.interactor.CommunalInteractor; -import com.android.systemui.communal.shared.model.CommunalSceneKey; -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState; +import com.android.systemui.communal.shared.model.CommunalScenes; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; @@ -842,16 +842,16 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test public void testEnteringGlanceableHub_updatesScrim() { // Transition to the glanceable hub. - mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( - CommunalSceneKey.Communal.INSTANCE))); + mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle( + CommunalScenes.Communal))); mTestScope.getTestScheduler().runCurrent(); // ScrimState also transitions. verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB); // Transition away from the glanceable hub. - mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( - CommunalSceneKey.Blank.INSTANCE))); + mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle( + CommunalScenes.Blank))); mTestScope.getTestScheduler().runCurrent(); // ScrimState goes back to UNLOCKED. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index eb890c7a78d0..443dd6a5dbc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -243,7 +243,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { statusContainer.dispatchTouchEvent( getActionUpEventFromSource(InputDevice.SOURCE_MOUSE) ) - verify(shadeViewController).expand(any()) + verify(shadeControllerImpl).animateExpandShade() } @Test @@ -272,7 +272,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { controller = createAndInitController(view) } view.performClick() - verify(shadeViewController, never()).expand(any()) + verify(shadeControllerImpl, never()).animateExpandShade() } private fun getCommandQueueCallback(): CommandQueue.Callbacks { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 3666248d1783..f050857d5df2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -86,6 +86,7 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeExpansionChangeEvent; @@ -224,7 +225,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { () -> mock(KeyguardDismissActionInteractor.class), mSelectedUserInteractor, () -> mock(KeyguardSurfaceBehindInteractor.class), - mock(JavaAdapter.class)) { + mock(JavaAdapter.class), + () -> mock(SceneInteractor.class)) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; @@ -733,7 +735,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { () -> mock(KeyguardDismissActionInteractor.class), mSelectedUserInteractor, () -> mock(KeyguardSurfaceBehindInteractor.class), - mock(JavaAdapter.class)) { + mock(JavaAdapter.class), + () -> mock(SceneInteractor.class)) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt new file mode 100644 index 000000000000..b8284acc997b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.os.UserHandle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeCameraAutoRotateRepository : CameraAutoRotateRepository { + private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>() + + /** Send a Unit signal when value changes */ + override fun isCameraAutoRotateSettingEnabled(userHandle: UserHandle): StateFlow<Boolean> = + getFlow(userHandle.identifier) + + fun setEnabled(userHandle: UserHandle, enabled: Boolean) { + getFlow(userHandle.identifier).value = enabled + } + + /** initializes the flow if already not */ + private fun getFlow(userId: Int): MutableStateFlow<Boolean> = + userMap.getOrPut(userId) { MutableStateFlow(false) } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryKosmos.kt index ad8ccb00f45f..615c59661f1d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryKosmos.kt @@ -14,12 +14,9 @@ * limitations under the License. */ -package com.android.systemui.volume.panel +package com.android.systemui.camera.data.repository import com.android.systemui.kosmos.Kosmos -import com.android.systemui.media.mediaOutputDialogFactory -import com.android.systemui.plugins.activityStarter -import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor -val Kosmos.mediaOutputActionsInteractor by - Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) } +val Kosmos.fakeCameraAutoRotateRepository: FakeCameraAutoRotateRepository by + Kosmos.Fixture { FakeCameraAutoRotateRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt new file mode 100644 index 000000000000..994e9b2f3683 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.camera.data.repository + +import android.os.UserHandle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeCameraSensorPrivacyRepository : CameraSensorPrivacyRepository { + + private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>() + override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> = + getFlow(userHandle.identifier) + + fun setEnabled(userHandle: UserHandle, enabled: Boolean) { + getFlow(userHandle.identifier).value = enabled + } + + /** initializes the flow if already not */ + private fun getFlow(userId: Int): MutableStateFlow<Boolean> = + userMap.getOrPut(userId) { MutableStateFlow(false) } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryKosmos.kt index c6ae21505c68..c7e704cab42f 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryKosmos.kt @@ -14,18 +14,9 @@ * limitations under the License. */ -package com.android.systemui.scene.shared.model +package com.android.systemui.camera.data.repository -data class UserActionResult( +import com.android.systemui.kosmos.Kosmos - /** The scene we should be transitioning due to the [UserAction]. */ - val toScene: SceneKey, - - /** - * The key of the transition that should be used, if a specific one should be used. - * - * If `null`, the transition used will be the corresponding transition from the collection - * passed into the UI layer. - */ - val transitionKey: TransitionKey? = null, -) +val Kosmos.fakeCameraSensorPrivacyRepository: FakeCameraSensorPrivacyRepository by + Kosmos.Fixture { FakeCameraSensorPrivacyRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt index 9d508d23dca7..5ff588f810bd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt @@ -1,7 +1,8 @@ package com.android.systemui.communal.data.repository -import com.android.systemui.communal.shared.model.CommunalSceneKey -import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.communal.shared.model.CommunalScenes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -16,17 +17,16 @@ import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) class FakeCommunalRepository( applicationScope: CoroutineScope, - override val desiredScene: MutableStateFlow<CommunalSceneKey> = - MutableStateFlow(CommunalSceneKey.DEFAULT), + override val desiredScene: MutableStateFlow<SceneKey> = + MutableStateFlow(CommunalScenes.Default), ) : CommunalRepository { - override fun setDesiredScene(desiredScene: CommunalSceneKey) { + override fun setDesiredScene(desiredScene: SceneKey) { this.desiredScene.value = desiredScene } - private val defaultTransitionState = - ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT) - private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null) - override val transitionState: StateFlow<ObservableCommunalTransitionState> = + private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) + private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null) + override val transitionState: StateFlow<ObservableTransitionState> = _transitionState .flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) } .stateIn( @@ -35,7 +35,7 @@ class FakeCommunalRepository( initialValue = defaultTransitionState, ) - override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) { + override fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { _transitionState.value = transitionState } } 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 1e305d67d40d..793e2d7efcda 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 @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.graphics.Point +import com.android.systemui.common.shared.model.Position import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource @@ -57,6 +58,9 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _bottomAreaAlpha = MutableStateFlow(1f) override val bottomAreaAlpha: StateFlow<Float> = _bottomAreaAlpha + private val _clockPosition = MutableStateFlow(Position(0, 0)) + override val clockPosition: StateFlow<Position> = _clockPosition + private val _isKeyguardShowing = MutableStateFlow(false) override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing @@ -145,6 +149,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _bottomAreaAlpha.value = alpha } + override fun setClockPosition(x: Int, y: Int) { + _clockPosition.value = Position(x, y) + } + fun setKeyguardShowing(isShowing: Boolean) { _isKeyguardShowing.value = isShowing } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt index d5d357f81b35..c2300a1ed1ad 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt @@ -43,6 +43,7 @@ val Kosmos.keyguardRootViewModel by Fixture { aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel, dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, + dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel, glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel, goneToAodTransitionViewModel = goneToAodTransitionViewModel, goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt new file mode 100644 index 000000000000..ecf8ce5bb8b8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.rotation + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger +import com.android.systemui.rotationlock.RotationLockNewModule + +val Kosmos.qsRotationLockTileConfig by + Kosmos.Fixture { RotationLockNewModule.provideRotationTileConfig(qsEventLogger) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt index 8fc419cadb21..2cdf76d50299 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt @@ -3,18 +3,18 @@ package com.android.systemui.scene import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.scene.shared.model.SceneContainerConfig -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.Scenes var Kosmos.sceneKeys by Fixture { listOf( - SceneKey.QuickSettings, - SceneKey.Shade, - SceneKey.Lockscreen, - SceneKey.Bouncer, - SceneKey.Gone, - SceneKey.Communal, + Scenes.QuickSettings, + Scenes.Shade, + Scenes.Lockscreen, + Scenes.Bouncer, + Scenes.Gone, + Scenes.Communal, ) } -val Kosmos.initialSceneKey by Fixture { SceneKey.Lockscreen } +val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen } val Kosmos.sceneContainerConfig by Fixture { SceneContainerConfig(sceneKeys, initialSceneKey) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt index c208aad78295..59a01cbedc5c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt @@ -16,6 +16,8 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorKosmos.kt new file mode 100644 index 000000000000..db6ba6284599 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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.stack.domain.interactor + +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.power.domain.interactor.powerInteractor + +val Kosmos.notificationStackInteractor by Fixture { + NotificationStackInteractor( + keyguardInteractor = keyguardInteractor, + powerInteractor = powerInteractor, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt index 25e3eac0b4b7..f1767ebb10d1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt @@ -16,16 +16,15 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.notificationShelfViewModel +import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackInteractor import com.android.systemui.statusbar.policy.domain.interactor.userSetupInteractor import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import java.util.Optional @@ -37,8 +36,7 @@ val Kosmos.notificationListViewModel by Fixture { Optional.of(footerViewModel), Optional.of(notificationListLoggerViewModel), activeNotificationsInteractor, - keyguardInteractor, - powerInteractor, + notificationStackInteractor, remoteInputInteractor, seenNotificationsInteractor, shadeInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt new file mode 100644 index 000000000000..89eaf15a52d9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 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.policy + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.devicePostureController by Kosmos.Fixture { mock<DevicePostureController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java index be57658a4266..4aa85a79c934 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java @@ -19,13 +19,29 @@ import android.testing.LeakCheck; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; +import java.util.ArrayList; +import java.util.List; + public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback> implements RotationLockController { + private boolean mIsLocked = false; + private final List<RotationLockControllerCallback> mCallbacks = new ArrayList<>(); public FakeRotationLockController(LeakCheck test) { super(test, "rotation"); } @Override + public void addCallback(RotationLockControllerCallback listener) { + mCallbacks.add(listener); + listener.onRotationLockStateChanged(mIsLocked, isRotationLockAffordanceVisible()); + } + + @Override + public void removeCallback(RotationLockControllerCallback listener) { + mCallbacks.remove(listener); + } + + @Override public void setListening(boolean listening) { } @@ -42,12 +58,15 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont @Override public boolean isRotationLocked() { - return false; + return mIsLocked; } @Override public void setRotationLocked(boolean locked, String caller) { - + mIsLocked = locked; + for (RotationLockControllerCallback callback : mCallbacks) { + callback.onRotationLockStateChanged(locked, isRotationLockAffordanceVisible()); + } } @Override diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt index 3f20df3376d9..a3b1a0eb260d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt @@ -25,7 +25,6 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.media.mediaOutputDialogFactory -import com.android.systemui.plugins.activityStarter import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -43,7 +42,7 @@ val Kosmos.localMediaRepositoryFactory: LocalMediaRepositoryFactory by Kosmos.Fixture { FakeLocalMediaRepositoryFactory { localMediaRepository } } val Kosmos.mediaOutputActionsInteractor by - Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) } + Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory) } val Kosmos.mediaControllerRepository by Kosmos.Fixture { FakeMediaControllerRepository() } val Kosmos.mediaOutputInteractor by Kosmos.Fixture { diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java index cc94090d9ec7..9057d163957e 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java @@ -42,7 +42,6 @@ public class ClassLoadHook { public static final String KEYBOARD_PATHS = "keyboard_paths"; public static final String GRAPHICS_NATIVE_CLASSES = "graphics_native_classes"; - public static final String VALUE_N_A = "**n/a**"; public static final String LIBANDROID_RUNTIME_NAME = "android_runtime"; private static String sInitialDir = new File("").getAbsolutePath(); @@ -130,8 +129,6 @@ public class ClassLoadHook { } setProperty(CORE_NATIVE_CLASSES, jniClasses); setProperty(GRAPHICS_NATIVE_CLASSES, ""); - setProperty(ICU_DATA_PATH, VALUE_N_A); - setProperty(KEYBOARD_PATHS, VALUE_N_A); RavenwoodUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index f4ea754c21ba..279bd72da6e7 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -1004,17 +1004,19 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH && overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); - } // TODO(b/319537921): should there be an else here? - //Primary pointer is swiping, so transit to PanningScalingState - transitToPanningScalingStateAndClear(); + } else { + //Primary pointer is swiping, so transit to PanningScalingState + transitToPanningScalingStateAndClear(); + } } else if (mIsSinglePanningEnabled && isActivated() && event.getPointerCount() == 1) { if (overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); - } // TODO(b/319537921): should there be an else here? - transitToSinglePanningStateAndClear(); + } else { + transitToSinglePanningStateAndClear(); + } } else if (!mIsTwoFingerCountReached) { // If it is a two-finger gesture, do not transition to the // delegating state to ensure the reachability of @@ -1257,17 +1259,19 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH && overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); + } else { + //Primary pointer is swiping, so transit to PanningScalingState + transitToPanningScalingStateAndClear(); } - //Primary pointer is swiping, so transit to PanningScalingState - transitToPanningScalingStateAndClear(); } else if (mIsSinglePanningEnabled && isActivated() && event.getPointerCount() == 1) { if (overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); + } else { + transitToSinglePanningStateAndClear(); } - transitToSinglePanningStateAndClear(); } else { transitionToDelegatingStateAndClear(); } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 6f45f60f6dfa..29b9d441cf38 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -476,8 +476,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } else { // If the package is being updated, we'll receive a PACKAGE_ADDED // shortly, otherwise it is removed permanently. - final boolean packageRemovedPermanently = (extras == null - || !extras.getBoolean(Intent.EXTRA_REPLACING, false)); + boolean isReplacing = extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, + false); + boolean isArchival = extras != null && extras.getBoolean(Intent.EXTRA_ARCHIVAL, + false); + final boolean packageRemovedPermanently = + (extras == null || !isReplacing || (isReplacing && isArchival)); if (packageRemovedPermanently) { for (String pkgName : pkgList) { @@ -2074,6 +2078,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, int viewId, long requestId) { try { + Slog.d(TAG, "Trying to notify widget view data changed"); callbacks.viewDataChanged(appWidgetId, viewId); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { @@ -2158,6 +2163,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks, int appWidgetId, RemoteViews views, long requestId) { try { + Slog.d(TAG, "Trying to notify widget update for package " + + (views == null ? "null" : views.getPackage()) + + " with widget id: " + appWidgetId); callbacks.updateAppWidget(appWidgetId, views); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { @@ -2196,6 +2204,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, AppWidgetProviderInfo info, long requestId) { try { + Slog.d(TAG, "Trying to notify provider update"); callbacks.providerChanged(appWidgetId, info); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { @@ -2239,6 +2248,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyAppWidgetRemoved(Host host, IAppWidgetHost callbacks, int appWidgetId, long requestId) { try { + Slog.d(TAG, "Trying to notify widget removed"); callbacks.appWidgetRemoved(appWidgetId); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { @@ -2286,6 +2296,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) { try { + Slog.d(TAG, "Trying to notify widget providers changed"); callbacks.providersChanged(); } catch (RemoteException re) { synchronized (mLock) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index d779fbf2eabc..551297b253d1 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -4782,7 +4782,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (isCredmanIntegrationActive(response)) { - Slog.d(TAG, "Attempting to add Credential Manager callback to pinned entries"); addCredentialManagerCallback(response); } @@ -5713,7 +5712,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /* isPrimary= */ true); updateFillDialogTriggerIdsLocked(); updateTrackedIdsLocked(); - if (mCurrentViewId == null) { return; } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index a478a3d84161..17ba0730c8e4 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -228,8 +228,6 @@ public class CompanionDeviceManagerService extends SystemService { /* cdmService */ this, mAssociationStore, mPersistentStore, mSystemDataTransferRequestStore, mAssociationRequestsProcessor); - loadAssociationsFromDisk(); - mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId()); mAssociationStore.registerListener(mAssociationStoreChangeListener); @@ -240,13 +238,18 @@ public class CompanionDeviceManagerService extends SystemService { mCompanionAppController = new CompanionApplicationController( context, mAssociationStore, mObservableUuidStore, mDevicePresenceMonitor, mPowerManagerInternal); + + mAssociationRevokeProcessor = new AssociationRevokeProcessor(this, mAssociationStore, + mPackageManagerInternal, mDevicePresenceMonitor, mCompanionAppController, + mSystemDataTransferRequestStore); + + loadAssociationsFromDisk(); + mTransportManager = new CompanionTransportManager(context, mAssociationStore); mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mPackageManagerInternal, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); - mAssociationRevokeProcessor = new AssociationRevokeProcessor(this, mAssociationStore, - mPackageManagerInternal, mDevicePresenceMonitor, mCompanionAppController, - mSystemDataTransferRequestStore); + // TODO(b/279663946): move context sync to a dedicated system service mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 9189ea763577..e1d7be121865 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -348,6 +348,9 @@ public class SystemConfig { // marked as stopped by the system @NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>(); + // Which packages (key) are allowed to join particular SharedUid (value). + @NonNull private final Map<String, String> mPackageToSharedUidAllowList = new ArrayMap<>(); + // A map of preloaded package names and the path to its app metadata file path. private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>(); @@ -567,6 +570,11 @@ public class SystemConfig { return mInitialNonStoppedSystemPackages; } + @NonNull + public Map<String, String> getPackageToSharedUidAllowList() { + return mPackageToSharedUidAllowList; + } + public ArrayMap<String, String> getAppMetadataFilePaths() { return mAppMetadataFilePaths; } @@ -1563,6 +1571,19 @@ public class SystemConfig { mInitialNonStoppedSystemPackages.add(pkgName); } } break; + case "allow-package-shareduid": { + String pkgName = parser.getAttributeValue(null, "package"); + String sharedUid = parser.getAttributeValue(null, "shareduid"); + if (TextUtils.isEmpty(pkgName)) { + Slog.w(TAG, "<" + name + "> without package in " + permFile + + " at " + parser.getPositionDescription()); + } else if (TextUtils.isEmpty(sharedUid)) { + Slog.w(TAG, "<" + name + "> without shareduid in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mPackageToSharedUidAllowList.put(pkgName, sharedUid); + } + } case "asl-file": { String packageName = parser.getAttributeValue(null, "package"); String path = parser.getAttributeValue(null, "path"); diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index 5e9d1cbb32ca..8dc15ade532e 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -163,6 +163,9 @@ } ], "file_patterns": ["PinnerService\\.java"] + }, + { + "name": "FrameworksVpnTests" } ] } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index b8e09cce93b9..258f53d982d2 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -372,6 +372,15 @@ public final class ActiveServices { @Overridable public static final long FGS_BOOT_COMPLETED_RESTRICTIONS = 296558535L; + /** + * Disables foreground service background starts in System Alert Window for all types + * unless it already has a System Overlay Window. + */ + @ChangeId + @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM) + @Overridable + public static final long FGS_SAW_RESTRICTIONS = 319471980L; + final ActivityManagerService mAm; // Maximum number of services that we allow to start in the background @@ -8526,10 +8535,31 @@ public final class ActiveServices { } } + // The flag being enabled isn't enough to deny background start: we need to also check + // if there is a system alert UI present. if (ret == REASON_DENIED) { - if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid, - callingPackage)) { - ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; + // Flag check: are we disabling SAW FGS background starts? + final boolean shouldDisableSaw = Flags.fgsDisableSaw() + && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, callingUid); + if (shouldDisableSaw) { + final ProcessRecord processRecord = mAm + .getProcessRecordLocked(targetService.processName, + targetService.appInfo.uid); + if (processRecord != null) { + if (processRecord.mState.hasOverlayUi()) { + if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid, + callingPackage)) { + ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; + } + } + } else { + Slog.e(TAG, "Could not find process record for SAW check"); + } + } else { + if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid, + callingPackage)) { + ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; + } } } diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index c06bdf90a75a..b1823b4ee38f 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -23,6 +23,13 @@ flag { } flag { + name: "fgs_disable_saw" + namespace: "backstage_power" + description: "Disable System Alert Window FGS start" + bug: "296558535" +} + +flag { name: "bfgs_managed_network_access" namespace: "backstage_power" description: "Restrict network access for certain applications in BFGS process state" diff --git a/services/core/java/com/android/server/biometrics/log/ALSProbe.java b/services/core/java/com/android/server/biometrics/log/ALSProbe.java index d584c99cea72..d4e46a930124 100644 --- a/services/core/java/com/android/server/biometrics/log/ALSProbe.java +++ b/services/core/java/com/android/server/biometrics/log/ALSProbe.java @@ -179,15 +179,18 @@ final class ALSProbe implements Probe { nextConsumer.consume(current); } else if (mNextConsumer != null) { mNextConsumer.add(nextConsumer); - } else { + } else if (mLightSensor != null) { mDestroyed = false; mNextConsumer = nextConsumer; enableLightSensorLoggingLocked(); + } else { + Slog.w(TAG, "No light sensor - use current to consume"); + nextConsumer.consume(current); } } private void enableLightSensorLoggingLocked() { - if (!mEnabled) { + if (!mEnabled && mLightSensor != null) { mEnabled = true; mLastAmbientLux = -1; mSensorManager.registerListener(mLightSensorListener, mLightSensor, @@ -201,7 +204,7 @@ final class ALSProbe implements Probe { private void disableLightSensorLoggingLocked(boolean destroying) { resetTimerLocked(false /* start */); - if (mEnabled) { + if (mEnabled && mLightSensor != null) { mEnabled = false; if (!destroying) { mLastAmbientLux = -1; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 6b8586a2e483..68b4e3fb51ba 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -119,7 +119,7 @@ public class FaceService extends SystemService { * Receives the incoming binder calls from FaceManager. */ @VisibleForTesting final class FaceServiceWrapper extends IFaceService.Stub { - @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC) + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { diff --git a/services/core/java/com/android/server/connectivity/TEST_MAPPING b/services/core/java/com/android/server/connectivity/TEST_MAPPING index 687d4b06b4c0..f50831964303 100644 --- a/services/core/java/com/android/server/connectivity/TEST_MAPPING +++ b/services/core/java/com/android/server/connectivity/TEST_MAPPING @@ -7,7 +7,7 @@ "exclude-annotation": "com.android.testutils.SkipPresubmit" } ], - "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"] + "file_patterns": ["VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"] } ], "presubmit-large": [ @@ -26,5 +26,10 @@ ], "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"] } + ], + "postsubmit":[ + { + "name":"FrameworksVpnTests" + } ] }
\ No newline at end of file diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index babc36efcca8..8e844501ab34 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -200,12 +200,9 @@ public class DisplayBrightnessStrategySelector { // We are not checking the targetDisplayState, but rather relying on the policy because // a user can define a different display state(displayPowerRequest.dozeScreenState) too // in the request with the Doze policy - if (displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE) { - if (!mAllowAutoBrightnessWhileDozingConfig) { - return true; - } - } - return false; + return displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE + && !mAllowAutoBrightnessWhileDozingConfig + && BrightnessUtils.isValidBrightnessValue(displayPowerRequest.dozeScreenBrightness); } @VisibleForTesting diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig new file mode 100644 index 000000000000..10b5eff06e0c --- /dev/null +++ b/services/core/java/com/android/server/flags/services.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.flags" + +flag { + namespace: "wear_frameworks" + name: "enable_odp_feature_guard" + description: "Enable guard based on system feature to prevent OnDevicePersonalization service from starting on form factors." + bug: "322249125" +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index a79e7715f064..05b1cb69235b 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -54,6 +54,7 @@ import android.hardware.input.InputManager; import android.hardware.input.InputSensorInfo; import android.hardware.input.InputSettings; import android.hardware.input.KeyboardLayout; +import android.hardware.input.KeyboardLayoutSelectionResult; import android.hardware.input.TouchCalibration; import android.hardware.lights.Light; import android.hardware.lights.LightState; @@ -1244,9 +1245,9 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public String getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, - @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, - @Nullable InputMethodSubtype imeSubtype) { + public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice( + InputDeviceIdentifier identifier, @UserIdInt int userId, + @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) { return mKeyboardLayoutManager.getKeyboardLayoutForInputDevice(identifier, userId, imeInfo, imeSubtype); } diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 46668de042d4..283e692ffbab 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -16,10 +16,11 @@ package com.android.server.input; -import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT; -import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE; -import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER; -import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD; +import static android.hardware.input.KeyboardLayoutSelectionResult.FAILED; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT; import android.annotation.AnyThread; import android.annotation.MainThread; @@ -46,6 +47,7 @@ import android.content.res.XmlResourceParser; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; +import android.hardware.input.KeyboardLayoutSelectionResult; import android.icu.lang.UScript; import android.icu.util.ULocale; import android.os.Bundle; @@ -79,7 +81,6 @@ import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent; -import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria; import com.android.server.inputmethod.InputMethodManagerInternal; import libcore.io.Streams; @@ -130,7 +131,8 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { // This cache stores "best-matched" layouts so that we don't need to run the matching // algorithm repeatedly. @GuardedBy("mKeyboardLayoutCache") - private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>(); + private final Map<String, KeyboardLayoutSelectionResult> mKeyboardLayoutCache = + new ArrayMap<>(); private final Object mImeInfoLock = new Object(); @Nullable @GuardedBy("mImeInfoLock") @@ -222,17 +224,17 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } else { Set<String> selectedLayouts = new HashSet<>(); List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping(); - List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>(); + List<KeyboardLayoutSelectionResult> resultList = new ArrayList<>(); boolean hasMissingLayout = false; for (ImeInfo imeInfo : imeInfoList) { // Check if the layout has been previously configured - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal( keyboardIdentifier, imeInfo); - boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null; + boolean noLayoutFound = result.getLayoutDescriptor() == null; if (!noLayoutFound) { - selectedLayouts.add(layoutInfo.mDescriptor); + selectedLayouts.add(result.getLayoutDescriptor()); } - layoutInfoList.add(layoutInfo); + resultList.add(result); hasMissingLayout |= noLayoutFound; } @@ -260,7 +262,7 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } if (shouldLogConfiguration) { - logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList, + logKeyboardConfigurationEvent(inputDevice, imeInfoList, resultList, !mDataStore.hasInputDeviceEntry(key)); } } finally { @@ -757,10 +759,10 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { String keyboardLayoutDescriptor; if (useNewSettingsUi()) { synchronized (mImeInfoLock) { - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal( new KeyboardIdentifier(identifier, languageTag, layoutType), mCurrentImeInfo); - keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor; + keyboardLayoutDescriptor = result.getLayoutDescriptor(); } } else { keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier); @@ -788,26 +790,26 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @AnyThread - @Nullable - public String getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, - @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, - @Nullable InputMethodSubtype imeSubtype) { + @NonNull + public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice( + InputDeviceIdentifier identifier, @UserIdInt int userId, + @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) { if (!useNewSettingsUi()) { Slog.e(TAG, "getKeyboardLayoutForInputDevice() API not supported"); - return null; + return FAILED; } InputDevice inputDevice = getInputDevice(identifier); if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { - return null; + return FAILED; } KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice); - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal( keyboardIdentifier, new ImeInfo(userId, imeInfo, imeSubtype)); if (DEBUG) { Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : " - + userId + ", subtype = " + imeSubtype + " -> " + layoutInfo); + + userId + ", subtype = " + imeSubtype + " -> " + result); } - return layoutInfo != null ? layoutInfo.mDescriptor : null; + return result; } @AnyThread @@ -942,13 +944,13 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @Nullable - private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal( + private KeyboardLayoutSelectionResult getKeyboardLayoutForInputDeviceInternal( KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) { String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString(); synchronized (mDataStore) { String layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), layoutKey); if (layout != null) { - return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER); + return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_USER); } } @@ -961,17 +963,17 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal( keyboardIdentifier, imeInfo); // Call auto-matching algorithm to find the best matching layout - KeyboardLayoutInfo layoutInfo = + KeyboardLayoutSelectionResult result = getDefaultKeyboardLayoutBasedOnImeInfo(keyboardIdentifier, imeInfo, layoutList); - mKeyboardLayoutCache.put(layoutKey, layoutInfo); - return layoutInfo; + mKeyboardLayoutCache.put(layoutKey, result); + return result; } } } - @Nullable - private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo( + @NonNull + private static KeyboardLayoutSelectionResult getDefaultKeyboardLayoutBasedOnImeInfo( KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) { Arrays.sort(layoutList); @@ -986,7 +988,7 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { + "vendor and product Ids. " + keyboardIdentifier + " : " + layout.getDescriptor()); } - return new KeyboardLayoutInfo(layout.getDescriptor(), + return new KeyboardLayoutSelectionResult(layout.getDescriptor(), LAYOUT_SELECTION_CRITERIA_DEVICE); } } @@ -1004,13 +1006,14 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { + "HW information (Language tag and Layout type). " + keyboardIdentifier + " : " + layoutDesc); } - return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE); + return new KeyboardLayoutSelectionResult(layoutDesc, + LAYOUT_SELECTION_CRITERIA_DEVICE); } } if (imeInfo == null || imeInfo.mImeSubtypeHandle == null || imeInfo.mImeSubtype == null) { // Can't auto select layout based on IME info is null - return null; + return FAILED; } InputMethodSubtype subtype = imeInfo.mImeSubtype; @@ -1027,9 +1030,10 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { + layoutDesc); } if (layoutDesc != null) { - return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD); + return new KeyboardLayoutSelectionResult(layoutDesc, + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD); } - return null; + return FAILED; } @Nullable @@ -1246,21 +1250,23 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice, - @NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList, + @NonNull List<ImeInfo> imeInfoList, + @NonNull List<KeyboardLayoutSelectionResult> resultList, boolean isFirstConfiguration) { - if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) { + if (imeInfoList.isEmpty() || resultList.isEmpty()) { return; } KeyboardConfigurationEvent.Builder configurationEventBuilder = new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration( isFirstConfiguration); for (int i = 0; i < imeInfoList.size(); i++) { - KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i); + KeyboardLayoutSelectionResult result = resultList.get(i); String layoutName = null; int layoutSelectionCriteria = LAYOUT_SELECTION_CRITERIA_DEFAULT; - if (layoutInfo != null && layoutInfo.mDescriptor != null) { - layoutSelectionCriteria = layoutInfo.mSelectionCriteria; - KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(layoutInfo.mDescriptor); + if (result != null && result.getLayoutDescriptor() != null) { + layoutSelectionCriteria = result.getSelectionCriteria(); + KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse( + result.getLayoutDescriptor()); if (d != null) { layoutName = d.keyboardLayoutName; } @@ -1477,33 +1483,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } - private static class KeyboardLayoutInfo { - @Nullable - private final String mDescriptor; - @LayoutSelectionCriteria - private final int mSelectionCriteria; - - private KeyboardLayoutInfo(@Nullable String descriptor, - @LayoutSelectionCriteria int selectionCriteria) { - mDescriptor = descriptor; - mSelectionCriteria = selectionCriteria; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof KeyboardLayoutInfo) { - return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor) - && mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria; - } - return false; - } - - @Override - public int hashCode() { - return 31 * mSelectionCriteria + mDescriptor.hashCode(); - } - } - private interface KeyboardLayoutVisitor { void visitKeyboardLayout(Resources resources, int keyboardLayoutResId, KeyboardLayout layout); diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java index f53b9411a6a7..b8ae737919d9 100644 --- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java +++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java @@ -16,14 +16,18 @@ package com.android.server.input; -import static java.lang.annotation.RetentionPolicy.SOURCE; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD; +import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT; +import static android.hardware.input.KeyboardLayoutSelectionResult.layoutSelectionCriteriaToString; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.role.RoleManager; import android.content.Intent; import android.hardware.input.KeyboardLayout; +import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria; import android.icu.util.ULocale; import android.text.TextUtils; import android.util.Log; @@ -40,7 +44,6 @@ import com.android.internal.os.KeyboardConfiguredProto.RepeatedKeyboardLayoutCon import com.android.internal.util.FrameworkStatsLog; import com.android.server.policy.ModifierShortcutManager; -import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -57,32 +60,6 @@ public final class KeyboardMetricsCollector { // (requires restart) private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - @Retention(SOURCE) - @IntDef(prefix = {"LAYOUT_SELECTION_CRITERIA_"}, value = { - LAYOUT_SELECTION_CRITERIA_UNSPECIFIED, - LAYOUT_SELECTION_CRITERIA_USER, - LAYOUT_SELECTION_CRITERIA_DEVICE, - LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, - LAYOUT_SELECTION_CRITERIA_DEFAULT - }) - public @interface LayoutSelectionCriteria { - } - - /** Unspecified layout selection criteria */ - public static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED = 0; - - /** Manual selection by user */ - public static final int LAYOUT_SELECTION_CRITERIA_USER = 1; - - /** Auto-detection based on device provided language tag and layout type */ - public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 2; - - /** Auto-detection based on IME provided language tag and layout type */ - public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 3; - - /** Default selection */ - public static final int LAYOUT_SELECTION_CRITERIA_DEFAULT = 4; - @VisibleForTesting static final String DEFAULT_LAYOUT_NAME = "Default"; @@ -629,30 +606,16 @@ public final class KeyboardMetricsCollector { @Override public String toString() { - return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = " + return "{keyboardLanguageTag = " + keyboardLanguageTag + + " keyboardLayoutType = " + KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType) - + " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = " - + getStringForSelectionCriteria(layoutSelectionCriteria) - + "imeLanguageTag = " + imeLanguageTag + " imeLayoutType = " - + KeyboardLayout.LayoutType.getLayoutNameFromValue(imeLayoutType) + "}"; - } - } - - private static String getStringForSelectionCriteria( - @LayoutSelectionCriteria int layoutSelectionCriteria) { - switch (layoutSelectionCriteria) { - case LAYOUT_SELECTION_CRITERIA_UNSPECIFIED: - return "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED"; - case LAYOUT_SELECTION_CRITERIA_USER: - return "LAYOUT_SELECTION_CRITERIA_USER"; - case LAYOUT_SELECTION_CRITERIA_DEVICE: - return "LAYOUT_SELECTION_CRITERIA_DEVICE"; - case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD: - return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD"; - case LAYOUT_SELECTION_CRITERIA_DEFAULT: - return "LAYOUT_SELECTION_CRITERIA_DEFAULT"; - default: - return "INVALID_CRITERIA"; + + " keyboardLayoutName = " + keyboardLayoutName + + " layoutSelectionCriteria = " + + layoutSelectionCriteriaToString(layoutSelectionCriteria) + + " imeLanguageTag = " + imeLanguageTag + + " imeLayoutType = " + KeyboardLayout.LayoutType.getLayoutNameFromValue( + imeLayoutType) + + "}"; } } diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java index b30f5ecb2dd5..6eae9a4bbe22 100644 --- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java +++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java @@ -229,7 +229,8 @@ public class FocusEventDebugView extends RelativeLayout { /** Report a key event to the debug view. */ @AnyThread public void reportKeyEvent(KeyEvent event) { - post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event))); + KeyEvent keyEvent = KeyEvent.obtain(event); + post(() -> handleKeyEvent(keyEvent)); } /** Report a motion event to the debug view. */ diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index 8826e3d2d345..73f1aad31d72 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -91,10 +91,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_SHOW_IME, statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE, - Objects.toString(mService.mCurFocusedWindow), + Objects.toString(mService.mImeBindingState.mFocusedWindow), InputMethodDebug.softInputDisplayReasonToString(reason), InputMethodDebug.softInputModeToString( - mService.mCurFocusedWindowSoftInputMode)); + mService.mImeBindingState.mFocusedWindowSoftInputMode)); } mService.onShowHideSoftInputRequested(true /* show */, showInputToken, reason, statsToken); @@ -122,10 +122,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_HIDE_IME, statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE, - Objects.toString(mService.mCurFocusedWindow), + Objects.toString(mService.mImeBindingState.mFocusedWindow), InputMethodDebug.softInputDisplayReasonToString(reason), InputMethodDebug.softInputModeToString( - mService.mCurFocusedWindowSoftInputMode)); + mService.mImeBindingState.mFocusedWindowSoftInputMode)); } mService.onShowHideSoftInputRequested(false /* show */, hideInputToken, reason, statsToken); @@ -207,7 +207,8 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @Override public boolean removeImeScreenshot(int displayId) { if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) { - mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow, + mService.onShowHideSoftInputRequested(false /* show */, + mService.mImeBindingState.mFocusedWindow, REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */); return true; } diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java new file mode 100644 index 000000000000..4c20c3b9784a --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod; + +import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME; +import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED; + +import android.annotation.Nullable; +import android.os.IBinder; +import android.util.Printer; +import android.util.proto.ProtoOutputStream; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import android.view.inputmethod.EditorInfo; + +import com.android.internal.inputmethod.InputMethodDebug; +import com.android.server.wm.WindowManagerInternal; + +/** + * Stores information related to one active IME client on one display. + */ +final class ImeBindingState { + + /** + * The last window token that we confirmed to be focused. This is always updated upon + * reports from the input method client. If the window state is already changed before the + * report is handled, this field just keeps the last value. + */ + @Nullable + final IBinder mFocusedWindow; + + /** + * {@link WindowManager.LayoutParams#softInputMode} of {@link #mFocusedWindow}. + * + * @see #mFocusedWindow + */ + @SoftInputModeFlags + final int mFocusedWindowSoftInputMode; + + /** + * The client by which {@link #mFocusedWindow} was reported. This gets updated whenever + * an + * IME-focusable window gained focus (without necessarily starting an input connection), + * while {@link InputMethodManagerService#mClient} only gets updated when we actually start an + * input connection. + * + * @see #mFocusedWindow + */ + @Nullable + final ClientState mFocusedWindowClient; + + /** + * The editor info by which {@link #mFocusedWindow} was reported. This differs from + * {@link InputMethodManagerService#mCurEditorInfo} the same way {@link #mFocusedWindowClient} + * differs from {@link InputMethodManagerService#mCurClient}. + * + * @see #mFocusedWindow + */ + @Nullable + final EditorInfo mFocusedWindowEditorInfo; + + void dumpDebug(ProtoOutputStream proto, WindowManagerInternal windowManagerInternal) { + proto.write(CUR_FOCUSED_WINDOW_NAME, + windowManagerInternal.getWindowName(mFocusedWindow)); + proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, + InputMethodDebug.softInputModeToString(mFocusedWindowSoftInputMode)); + } + + void dump(String prefix, Printer p) { + p.println(prefix + "mFocusedWindow()=" + mFocusedWindow); + p.println(prefix + "softInputMode=" + InputMethodDebug.softInputModeToString( + mFocusedWindowSoftInputMode)); + p.println(prefix + "mFocusedWindowClient=" + mFocusedWindowClient); + } + + static ImeBindingState newEmptyState() { + return new ImeBindingState( + /*focusedWindow=*/ null, + /*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED, + /*focusedWindowClient=*/ null, + /*focusedWindowEditorInfo=*/ null + ); + } + + ImeBindingState(@Nullable IBinder focusedWindow, + @SoftInputModeFlags int focusedWindowSoftInputMode, + @Nullable ClientState focusedWindowClient, + @Nullable EditorInfo focusedWindowEditorInfo) { + mFocusedWindow = focusedWindow; + mFocusedWindowSoftInputMode = focusedWindowSoftInputMode; + mFocusedWindowClient = focusedWindowClient; + mFocusedWindowEditorInfo = focusedWindowEditorInfo; + } +} diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 743b8e382347..cdfde87f042f 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -548,7 +548,7 @@ public final class ImeVisibilityStateComputer { } } // Fallback to the focused window for some edge cases (e.g. relaunching the activity) - return mService.mCurFocusedWindow; + return mService.mImeBindingState.mFocusedWindow; } IBinder getWindowTokenFrom(ImeTargetWindowState windowState) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1564b2f86218..2205986fe9c9 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -28,7 +28,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DIS import static android.server.inputmethod.InputMethodManagerServiceProto.BOUND_TO_METHOD; import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ATTRIBUTE; import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_CLIENT; -import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME; import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE; import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ID; import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_METHOD_ID; @@ -40,7 +39,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.IME_WIND import static android.server.inputmethod.InputMethodManagerServiceProto.IN_FULLSCREEN_MODE; import static android.server.inputmethod.InputMethodManagerServiceProto.IS_INTERACTIVE; import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_IME_TARGET_WINDOW_NAME; -import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_SWITCH_USER_ID; import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_IME_WITH_HARD_KEYBOARD; import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_READY; import static android.view.Display.DEFAULT_DISPLAY; @@ -277,9 +275,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String[] mNonPreemptibleInputMethods; - @UserIdInt - private int mLastSwitchUserId; - final Context mContext; final Resources mRes; private final Handler mHandler; @@ -423,6 +418,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private final ClientController mClientController; /** + * Holds the current IME binding state info. + */ + ImeBindingState mImeBindingState; + + /** * Set once the system is ready to run third party code. */ boolean mSystemReady; @@ -482,13 +482,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private ClientState mCurClient; /** - * The last window token that we confirmed to be focused. This is always updated upon reports - * from the input method client. If the window state is already changed before the report is - * handled, this field just keeps the last value. - */ - IBinder mCurFocusedWindow; - - /** * The last window token that we confirmed that IME started talking to. This is always updated * upon reports from the input method. If the window state is already changed before the report * is handled, this field just keeps the last value. @@ -496,34 +489,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub IBinder mLastImeTargetWindow; /** - * {@link LayoutParams#softInputMode} of {@link #mCurFocusedWindow}. - * - * @see #mCurFocusedWindow - */ - @SoftInputModeFlags - int mCurFocusedWindowSoftInputMode; - - /** - * The client by which {@link #mCurFocusedWindow} was reported. This gets updated whenever an - * IME-focusable window gained focus (without necessarily starting an input connection), - * while {@link #mCurClient} only gets updated when we actually start an input connection. - * - * @see #mCurFocusedWindow - */ - @Nullable - ClientState mCurFocusedWindowClient; - - /** - * The editor info by which {@link #mCurFocusedWindow} was reported. This differs from - * {@link #mCurEditorInfo} the same way {@link #mCurFocusedWindowClient} differs - * from {@link #mCurClient}. - * - * @see #mCurFocusedWindow - */ - @Nullable - EditorInfo mCurFocusedWindowEditorInfo; - - /** * The {@link IRemoteInputConnection} last provided by the current client. */ IRemoteInputConnection mCurInputConnection; @@ -1131,10 +1096,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard( accessibilitySoftKeyboardSetting); if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) { - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE); } else if (isShowRequestedForCurrentWindow()) { - showCurrentInputLocked(mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, + showCurrentInputLocked(mImeBindingState.mFocusedWindow, + InputMethodManager.SHOW_IMPLICIT, SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } } else if (stylusHandwritingEnabledUri.equals(uri)) { @@ -1341,20 +1307,35 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onPackageDataCleared(String packageName, int uid) { + final int userId = getChangingUserId(); synchronized (ImfLock.class) { + final boolean isCurrentUser = (userId == mSettings.getUserId()); + final AdditionalSubtypeMap additionalSubtypeMap; + final InputMethodSettings settings; + if (isCurrentUser) { + additionalSubtypeMap = mAdditionalSubtypeMap; + settings = mSettings; + } else { + additionalSubtypeMap = AdditionalSubtypeUtils.load(userId); + settings = queryInputMethodServicesInternal(mContext, userId, + additionalSubtypeMap, DirectBootAwareness.AUTO); + } + // Note that one package may implement multiple IMEs. final ArrayList<String> changedImes = new ArrayList<>(); - for (InputMethodInfo imi : mSettings.getMethodList()) { + for (InputMethodInfo imi : settings.getMethodList()) { if (imi.getPackageName().equals(packageName)) { changedImes.add(imi.getId()); } } final AdditionalSubtypeMap newMap = - mAdditionalSubtypeMap.cloneWithRemoveOrSelf(changedImes); - if (newMap != mAdditionalSubtypeMap) { - mAdditionalSubtypeMap = newMap; + additionalSubtypeMap.cloneWithRemoveOrSelf(changedImes); + if (newMap != additionalSubtypeMap) { + if (isCurrentUser) { + mAdditionalSubtypeMap = newMap; + } AdditionalSubtypeUtils.save( - mAdditionalSubtypeMap, mSettings.getMethodMap(), mSettings.getUserId()); + newMap, settings.getMethodMap(), settings.getUserId()); } if (!changedImes.isEmpty()) { mChangedPackages.add(packageName); @@ -1629,7 +1610,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } // Hide soft input before user switch task since switch task may block main handler a while // and delayed the hideCurrentInputLocked(). - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_SWITCH_USER); final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId, clientToBeReset); @@ -1679,8 +1660,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final int userId = mActivityManagerInternal.getCurrentUserId(); - mLastSwitchUserId = userId; - // mSettings should be created before buildInputMethodListLocked mSettings = InputMethodSettings.createEmptyMap(userId); @@ -1704,6 +1683,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mClientController = new ClientController(mPackageManagerInternal); synchronized (ImfLock.class) { mClientController.addClientControllerCallback(c -> onClientRemoved(c)); + mImeBindingState = ImeBindingState.newEmptyState(); } mPreventImeStartupUnlessTextEditor = mRes.getBoolean( @@ -1866,7 +1846,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub + " selectedIme=" + mSettings.getSelectedInputMethod()); } - mLastSwitchUserId = newUserId; if (mIsInteractive && clientToBeReset != null) { final ClientState cs = mClientController.getClient(clientToBeReset.asBinder()); if (cs == null) { @@ -2218,7 +2197,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub clearClientSessionLocked(client); clearClientSessionForAccessibilityLocked(client); if (mCurClient == client) { - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); if (mBoundToMethod) { mBoundToMethod = false; @@ -2232,9 +2211,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mBoundToAccessibility = false; mCurClient = null; - if (mCurFocusedWindowClient == client) { - mCurFocusedWindowClient = null; - mCurFocusedWindowEditorInfo = null; + if (mImeBindingState.mFocusedWindowClient == client) { + mImeBindingState = ImeBindingState.newEmptyState(); } } } @@ -2283,7 +2261,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void onUnbindCurrentMethodByReset() { final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull( - mCurFocusedWindow); + mImeBindingState.mFocusedWindow); if (winState != null && !winState.isRequestedImeVisible() && !mVisibilityStateComputer.isInputShown()) { // Normally, the focus window will apply the IME visibility state to @@ -2295,7 +2273,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // binding states in the first place. final var statsToken = createStatsTokenForFocusedClient(false /* show */, SoftInputShowHideReason.UNBIND_CURRENT_METHOD); - mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken, STATE_HIDE_IME); + mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken, + STATE_HIDE_IME); } } @@ -2328,7 +2307,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") private boolean isShowRequestedForCurrentWindow() { final ImeTargetWindowState state = mVisibilityStateComputer.getWindowStateOrNull( - mCurFocusedWindow); + mImeBindingState.mFocusedWindow); return state != null && state.isRequestedImeVisible(); } @@ -2346,10 +2325,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub getCurTokenLocked(), mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting, UserHandle.getUserId(mCurClient.mUid), - mCurClient.mSelfReportedDisplayId, - mCurFocusedWindow, mCurEditorInfo, mCurFocusedWindowSoftInputMode, - getSequenceNumberLocked()); - mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow); + mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo, + mImeBindingState.mFocusedWindowSoftInputMode, getSequenceNumberLocked()); + mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow); mStartInputHistory.addEntry(info); // Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user @@ -2376,7 +2354,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub : createStatsTokenForFocusedClient(true /* show */, SoftInputShowHideReason.ATTACH_NEW_INPUT); mCurStatsToken = null; - showCurrentInputLocked(mCurFocusedWindow, statsToken, + showCurrentInputLocked(mImeBindingState.mFocusedWindow, statsToken, mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); } @@ -2450,7 +2428,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Compute the final shown display ID with validated cs.selfReportedDisplayId for this // session & other conditions. ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull( - mCurFocusedWindow); + mImeBindingState.mFocusedWindow); if (winState == null) { return InputBindResult.NOT_IME_TARGET_WINDOW; } @@ -2469,7 +2447,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE); return InputBindResult.NO_IME; } @@ -3638,7 +3616,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Binder.withCleanCallingIdentity(() -> { Objects.requireNonNull(windowToken, "windowToken must not be null"); synchronized (ImfLock.class) { - if (mCurFocusedWindow != windowToken || mCurPerceptible == perceptible) { + if (mImeBindingState.mFocusedWindow != windowToken + || mCurPerceptible == perceptible) { return; } mCurPerceptible = perceptible; @@ -3732,7 +3711,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub super.hideSoftInputFromServerForTest_enforcePermission(); synchronized (ImfLock.class) { - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT); } } @@ -3907,7 +3886,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid); final boolean showForced = mVisibilityStateComputer.mShowForced; - if (mCurFocusedWindow != windowToken && showForced && shouldClearFlag) { + if (mImeBindingState.mFocusedWindow != windowToken + && showForced && shouldClearFlag) { mVisibilityStateComputer.mShowForced = false; } @@ -3925,7 +3905,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + " a background user, use EditorInfo.targetInputMethodUser with" + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER); return InputBindResult.INVALID_USER; } @@ -3986,7 +3966,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub + " cs=" + cs); } - final boolean sameWindowFocused = mCurFocusedWindow == windowToken; + final boolean sameWindowFocused = mImeBindingState.mFocusedWindow == windowToken; final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; final boolean startInputByWinGainedFocus = (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0; @@ -4017,10 +3997,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub null, null, null, null, -1, false); } - mCurFocusedWindow = windowToken; - mCurFocusedWindowSoftInputMode = softInputMode; - mCurFocusedWindowClient = cs; - mCurFocusedWindowEditorInfo = editorInfo; + mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo); mCurPerceptible = true; // We want to start input before showing the IME, but after closing @@ -4051,9 +4028,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub break; } final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason()); - mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken, + mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken, imeVisRes.getState(), imeVisRes.getReason()); - if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) { // If focused display changed, we should unbind current method // to make app window in previous display relayout after Ime @@ -4092,7 +4068,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("unknown client " + client.asBinder()); } ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN); - if (!isImeClientFocused(mCurFocusedWindow, cs)) { + if (!isImeClientFocused(mImeBindingState.mFocusedWindow, cs)) { Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client)); return false; } @@ -4104,8 +4080,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { final int uid = Binder.getCallingUid(); - if (mCurFocusedWindowClient != null && client != null - && mCurFocusedWindowClient.mClient.asBinder() == client.asBinder()) { + if (mImeBindingState.mFocusedWindowClient != null && client != null + && mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) { return true; } if (mSettings.getUserId() != UserHandle.getUserId(uid)) { @@ -4728,12 +4704,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked()); proto.write(CUR_SEQ, getSequenceNumberLocked()); proto.write(CUR_CLIENT, Objects.toString(mCurClient)); - proto.write(CUR_FOCUSED_WINDOW_NAME, - mWindowManagerInternal.getWindowName(mCurFocusedWindow)); + mImeBindingState.dumpDebug(proto, mWindowManagerInternal); proto.write(LAST_IME_TARGET_WINDOW_NAME, mWindowManagerInternal.getWindowName(mLastImeTargetWindow)); - proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, - InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)); + proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString( + mImeBindingState.mFocusedWindowSoftInputMode)); if (mCurEditorInfo != null) { mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE); } @@ -4743,7 +4718,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked())); proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId); proto.write(SYSTEM_READY, mSystemReady); - proto.write(LAST_SWITCH_USER_ID, mLastSwitchUserId); proto.write(HAVE_CONNECTION, hasConnectionLocked()); proto.write(BOUND_TO_METHOD, mBoundToMethod); proto.write(IS_INTERACTIVE, mIsInteractive); @@ -4855,12 +4829,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken); final WindowManagerInternal.ImeTargetInfo info = mWindowManagerInternal.onToggleImeRequested( - show, mCurFocusedWindow, requestToken, mCurTokenDisplayId); + show, mImeBindingState.mFocusedWindow, requestToken, mCurTokenDisplayId); mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( - mCurFocusedWindowClient, mCurFocusedWindowEditorInfo, info.focusedWindowName, - mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, - info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName, - info.imeSurfaceParentName)); + mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo, + info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason, + mInFullscreenMode, info.requestWindowName, info.imeControlTargetName, + info.imeLayerTargetName, info.imeSurfaceParentName)); if (statsToken != null) { mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName); @@ -5031,8 +5005,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_HIDE_ALL_INPUT_METHODS: synchronized (ImfLock.class) { @SoftInputShowHideReason final int reason = (int) msg.obj; - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, reason); - + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, reason); } return true; case MSG_REMOVE_IME_SURFACE: { @@ -5051,7 +5024,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub IBinder windowToken = (IBinder) msg.obj; synchronized (ImfLock.class) { try { - if (windowToken == mCurFocusedWindow + if (windowToken == mImeBindingState.mFocusedWindow && mEnabledSession != null && mEnabledSession.mSession != null) { mEnabledSession.mSession.removeImeSurface(); } @@ -5126,7 +5099,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_START_HANDWRITING: synchronized (ImfLock.class) { IInputMethodInvoker curMethod = getCurMethodLocked(); - if (curMethod == null || mCurFocusedWindow == null) { + if (curMethod == null || mImeBindingState.mFocusedWindow == null) { return true; } final HandwritingModeController.HandwritingSession session = @@ -5134,7 +5107,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub msg.arg1 /*requestId*/, msg.arg2 /*pid*/, mBindingController.getCurMethodUid(), - mCurFocusedWindow); + mImeBindingState.mFocusedWindow); if (session == null) { Slog.e(TAG, "Failed to start handwriting session for requestId: " + msg.arg1); @@ -5187,11 +5160,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Handle IME visibility when interactive changed before finishing the input to // ensure we preserve the last state as possible. final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged( - mCurFocusedWindow, interactive); + mImeBindingState.mFocusedWindow, interactive); if (imeVisRes != null) { // Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker. - mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */, - imeVisRes.getState(), imeVisRes.getReason()); + mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, + null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason()); } // Eligible IME processes use new "setInteractive" protocol. mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode); @@ -5881,7 +5854,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void reportImeControl(@Nullable IBinder windowToken) { synchronized (ImfLock.class) { - if (mCurFocusedWindow != windowToken) { + if (mImeBindingState.mFocusedWindow != windowToken) { // mCurPerceptible was set by the focused window, but it is no longer in // control, so we reset mCurPerceptible. mCurPerceptible = true; @@ -5895,7 +5868,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Hide the IME method menu only when the IME surface parent is changed by the // input target changed, in case seeing the dialog dismiss flickering during // the next focused window starting the input connection. - if (mLastImeTargetWindow != mCurFocusedWindow) { + if (mLastImeTargetWindow != mImeBindingState.mFocusedWindow) { mMenuController.hideInputMethodMenuLocked(); } } @@ -6189,11 +6162,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); p.println(" mCurPerceptible=" + mCurPerceptible); - p.println(" mCurFocusedWindow=" + mCurFocusedWindow - + " softInputMode=" - + InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode) - + " client=" + mCurFocusedWindowClient); - focusedWindowClient = mCurFocusedWindowClient; + mImeBindingState.dump(" ", p); p.println(" mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked() + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + mBindingController.isVisibleBound()); @@ -6244,7 +6213,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println("No input method client."); } - if (focusedWindowClient != null && client != focusedWindowClient) { + if (mImeBindingState.mFocusedWindowClient != null + && client != mImeBindingState.mFocusedWindowClient) { p.println(" "); p.println("Warning: Current input method client doesn't match the last focused. " + "window."); @@ -6252,7 +6222,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println(" "); pw.flush(); try { - TransferPipe.dumpAsync(focusedWindowClient.mClient.asBinder(), fd, args); + TransferPipe.dumpAsync( + mImeBindingState.mFocusedWindowClient.mClient.asBinder(), fd, args); } catch (IOException | RemoteException e) { p.println("Failed to dump input method client in focused window: " + e); } @@ -6324,8 +6295,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @ShellCommandResult private int onCommandWithSystemIdentity(@Nullable String cmd) { switch (TextUtils.emptyIfNull(cmd)) { - case "get-last-switch-user-id": - return mService.getLastSwitchUserId(this); case "tracing": return mService.handleShellCommandTraceInputMethod(this); case "ime": { // For "adb shell ime <command>". @@ -6439,15 +6408,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- // Shell command handlers: - @BinderThread - @ShellCommandResult - private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) { - synchronized (ImfLock.class) { - shellCommand.getOutPrintWriter().println(mLastSwitchUserId); - return ShellCommandResult.SUCCESS; - } - } - /** * Handles {@code adb shell ime list}. * @param shellCommand {@link ShellCommand} object that is handling this command. @@ -6698,7 +6658,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final String nextIme; final List<InputMethodInfo> nextEnabledImes; if (userId == mSettings.getUserId()) { - hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); mBindingController.unbindCurrentMethod(); @@ -6832,11 +6792,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private ImeTracker.Token createStatsTokenForFocusedClient(boolean show, @SoftInputShowHideReason int reason) { - final int uid = mCurFocusedWindowClient != null - ? mCurFocusedWindowClient.mUid + final int uid = mImeBindingState.mFocusedWindowClient != null + ? mImeBindingState.mFocusedWindowClient.mUid : -1; - final var packageName = mCurFocusedWindowEditorInfo != null - ? mCurFocusedWindowEditorInfo.packageName + final var packageName = mImeBindingState.mFocusedWindowEditorInfo != null + ? mImeBindingState.mFocusedWindowEditorInfo.packageName : "uid(" + uid + ")"; return ImeTracker.forLogging().onStart(packageName, uid, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3a7ac0bd659d..ba5882cc7e98 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -12050,11 +12050,12 @@ public class NotificationManagerService extends SystemService { @Override public void onServiceAdded(ManagedServiceInfo info) { if (lifetimeExtensionRefactor()) { - // Generally, only System or System UI should have the permissions to call - // registerSystemService. - // isCallerSystemorPhone tells us whether the caller is System. Then, if it's not - // the system, we know it's system UI. - info.isSystemUi = !isCallerSystemOrPhone(); + // We explicitly check the status bar permission for the uid in the info object. + // We can't use the calling uid here because it's probably always system server. + // Note that this will also be true for the shell. + info.isSystemUi = getContext().checkPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, -1, info.uid) + == PERMISSION_GRANTED; } final INotificationListener listener = (INotificationListener) info.service; final NotificationRankingUpdate update; diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java index b9a267f37ff9..8e37b4f24880 100644 --- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java +++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java @@ -32,6 +32,7 @@ import android.app.NotificationManager; import android.content.pm.PackageManager; import android.os.Process; import android.service.notification.DNDPolicyProto; +import android.service.notification.ZenAdapters; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ConfigChangeOrigin; import android.service.notification.ZenModeConfig.ZenRule; @@ -591,9 +592,11 @@ class ZenModeEventLogger { // This applies to both call and message senders, but not conversation senders, // where they use the same enum values. proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, - ZenModeConfig.getZenPolicySenders(mNewPolicy.allowCallsFrom())); + ZenAdapters.notificationPolicySendersToZenPolicyPeopleType( + mNewPolicy.allowCallsFrom())); proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, - ZenModeConfig.getZenPolicySenders(mNewPolicy.allowMessagesFrom())); + ZenAdapters.notificationPolicySendersToZenPolicyPeopleType( + mNewPolicy.allowMessagesFrom())); proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, mNewPolicy.allowConversationsFrom()); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 6857869e3776..bc86c8216952 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -84,6 +84,7 @@ import android.provider.Settings.Global; import android.service.notification.Condition; import android.service.notification.ConditionProviderService; import android.service.notification.DeviceEffectsApplier; +import android.service.notification.ZenAdapters; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ConfigChangeOrigin; diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java index a4c4347b7cef..0abe50f12772 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java @@ -16,6 +16,8 @@ package com.android.server.ondeviceintelligence; +import static android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException.PROCESSING_UPDATE_STATUS_CONNECTION_FAILED; + import android.Manifest; import android.annotation.NonNull; import android.app.AppGlobals; @@ -36,6 +38,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.os.Bundle; import android.os.ICancellationSignal; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; @@ -43,8 +46,12 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.service.ondeviceintelligence.IOnDeviceIntelligenceService; import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService; +import android.service.ondeviceintelligence.IRemoteProcessingService; import android.service.ondeviceintelligence.IRemoteStorageService; +import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback; +import android.service.ondeviceintelligence.OnDeviceIntelligenceService; import android.text.TextUtils; import android.util.Slog; @@ -59,10 +66,11 @@ import java.util.Objects; import java.util.Set; /** - * This is the system service for handling calls on the {@link OnDeviceIntelligenceManager}. This + * This is the system service for handling calls on the + * {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}. This * service holds connection references to the underlying remote services i.e. the isolated service - * {@link android.service.ondeviceintelligence.OnDeviceTrustedInferenceService} and a regular - * service counter part {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService}. + * {@link OnDeviceTrustedInferenceService} and a regular + * service counter part {@link OnDeviceIntelligenceService}. * * Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of * the Inference service for each user, due to possible high memory footprint. @@ -313,10 +321,48 @@ public class OnDeviceIntelligenceManagerService extends SystemService { mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext, ComponentName.unflattenFromString(serviceName), UserHandle.SYSTEM.getIdentifier()); + mRemoteOnDeviceIntelligenceService.setServiceLifecycleCallbacks( + new ServiceConnector.ServiceLifecycleCallbacks<>() { + @Override + public void onConnected( + @NonNull IOnDeviceIntelligenceService service) { + try { + service.registerRemoteServices( + getRemoteProcessingService()); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to send connected event", ex); + } + } + }); } } } + @NonNull + private IRemoteProcessingService.Stub getRemoteProcessingService() { + return new IRemoteProcessingService.Stub() { + @Override + public void updateProcessingState( + Bundle processingState, + IProcessingUpdateStatusCallback callback) { + try { + ensureRemoteTrustedInferenceServiceInitialized(); + mRemoteInferenceService.post( + service -> service.updateProcessingState( + processingState, callback)); + } catch (RemoteException unused) { + try { + callback.onFailure( + PROCESSING_UPDATE_STATUS_CONNECTION_FAILED, + "Received failure invoking the remote processing service."); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to send failure status.", ex); + } + } + } + }; + } + private void ensureRemoteTrustedInferenceServiceInitialized() throws RemoteException { synchronized (mLock) { if (mRemoteInferenceService == null) { @@ -332,6 +378,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { public void onConnected( @NonNull IOnDeviceTrustedInferenceService service) { try { + ensureRemoteIntelligenceServiceInitialized(); service.registerRemoteStorageService( getIRemoteStorageService()); } catch (RemoteException ex) { @@ -358,8 +405,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { @Override public void getReadOnlyFeatureFileDescriptorMap( Feature feature, - RemoteCallback remoteCallback) - throws RemoteException { + RemoteCallback remoteCallback) { mRemoteOnDeviceIntelligenceService.post( service -> service.getReadOnlyFeatureFileDescriptorMap( feature, remoteCallback)); diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 8452c0e61a81..4f86adfe2d8d 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -21,7 +21,6 @@ import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import android.Manifest; import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.app.role.RoleManager; @@ -39,6 +38,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; @@ -96,6 +96,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000; private final Object mLock = new Object(); + private final Injector mInjector; private final Context mContext; private final AppOpsManager mAppOps; private final TelephonyManager mTelephonyManager; @@ -345,6 +346,14 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { AtomicFile getMappingFile() { return mMappingFile; } + + UserManager getUserManager() { + return mContext.getSystemService(UserManager.class); + } + + DevicePolicyManager getDevicePolicyManager() { + return mContext.getSystemService(DevicePolicyManager.class); + } } BugreportManagerServiceImpl(Context context) { @@ -356,6 +365,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) BugreportManagerServiceImpl(Injector injector) { + mInjector = injector; mContext = injector.getContext(); mAppOps = mContext.getSystemService(AppOpsManager.class); mTelephonyManager = mContext.getSystemService(TelephonyManager.class); @@ -388,12 +398,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { int callingUid = Binder.getCallingUid(); enforcePermission(callingPackage, callingUid, bugreportMode == BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */); - final long identity = Binder.clearCallingIdentity(); - try { - ensureUserCanTakeBugReport(bugreportMode); - } finally { - Binder.restoreCallingIdentity(identity); - } + ensureUserCanTakeBugReport(bugreportMode); Slogf.i(TAG, "Starting bugreport for %s / %d", callingPackage, callingUid); synchronized (mLock) { @@ -432,7 +437,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @RequiresPermission(value = Manifest.permission.DUMP, conditional = true) public void retrieveBugreport(int callingUidUnused, String callingPackage, int userId, FileDescriptor bugreportFd, String bugreportFile, - boolean keepBugreportOnRetrievalUnused, IDumpstateListener listener) { int callingUid = Binder.getCallingUid(); enforcePermission(callingPackage, callingUid, false); @@ -564,54 +568,59 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } /** - * Validates that the current user is an admin user or, when bugreport is requested remotely - * that the current user is an affiliated user. + * Validates that the calling user is an admin user or, when bugreport is requested remotely + * that the user is an affiliated user. * - * @throws IllegalArgumentException if the current user is not an admin user + * @throws IllegalArgumentException if the calling user or the parent of the calling profile + * user is not an admin user. */ private void ensureUserCanTakeBugReport(int bugreportMode) { - UserInfo currentUser = null; + // Get the calling userId before clearing the caller identity. + int effectiveCallingUserId = UserHandle.getUserId(Binder.getCallingUid()); + boolean isAdminUser = false; + final long identity = Binder.clearCallingIdentity(); try { - currentUser = ActivityManager.getService().getCurrentUser(); - } catch (RemoteException e) { - // Impossible to get RemoteException for an in-process call. - } - - if (currentUser == null) { - logAndThrow("There is no current user, so no bugreport can be requested."); + UserInfo profileParent = + mInjector.getUserManager().getProfileParent(effectiveCallingUserId); + if (profileParent == null) { + isAdminUser = mInjector.getUserManager().isUserAdmin(effectiveCallingUserId); + } else { + // If the caller is a profile, we need to check its parent user instead. + // Therefore setting the profile parent user as the effective calling user. + effectiveCallingUserId = profileParent.id; + isAdminUser = profileParent.isAdmin(); + } + } finally { + Binder.restoreCallingIdentity(identity); } - - if (!currentUser.isAdmin()) { + if (!isAdminUser) { if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE - && isCurrentUserAffiliated(currentUser.id)) { + && isUserAffiliated(effectiveCallingUserId)) { return; } - logAndThrow(TextUtils.formatSimple("Current user %s is not an admin user." - + " Only admin users are allowed to take bugreport.", currentUser.id)); + logAndThrow(TextUtils.formatSimple("Calling user %s is not an admin user." + + " Only admin users and their profiles are allowed to take bugreport.", + effectiveCallingUserId)); } } /** - * Returns {@code true} if the device has device owner and the current user is affiliated + * Returns {@code true} if the device has device owner and the specified user is affiliated * with the device owner. */ - private boolean isCurrentUserAffiliated(int currentUserId) { - DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + private boolean isUserAffiliated(int userId) { + DevicePolicyManager dpm = mInjector.getDevicePolicyManager(); int deviceOwnerUid = dpm.getDeviceOwnerUserId(); if (deviceOwnerUid == UserHandle.USER_NULL) { return false; } - int callingUserId = UserHandle.getUserId(Binder.getCallingUid()); - - Slog.i(TAG, "callingUid: " + callingUserId + " deviceOwnerUid: " + deviceOwnerUid - + " currentUserId: " + currentUserId); - - if (callingUserId != deviceOwnerUid) { - logAndThrow("Caller is not device owner on provisioned device."); + if (DEBUG) { + Slog.d(TAG, "callingUid: " + userId + " deviceOwnerUid: " + deviceOwnerUid); } - if (!dpm.isAffiliatedUser(currentUserId)) { - logAndThrow("Current user is not affiliated to the device owner."); + + if (userId != deviceOwnerUid && !dpm.isAffiliatedUser(userId)) { + logAndThrow("User " + userId + " is not affiliated to the device owner."); } return true; } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 186cf5e37003..4bfd077760e4 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -1080,7 +1080,7 @@ final class InstallPackageHelper { reconciledPackages = ReconcilePackageUtils.reconcilePackages( requests, Collections.unmodifiableMap(mPm.mPackages), versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(), - mPm.mSettings); + mPm.mSettings, mPm.mInjector.getSystemConfig()); } catch (ReconcileFailure e) { for (InstallRequest request : requests) { request.setError("Reconciliation failed...", e); @@ -3810,7 +3810,7 @@ final class InstallPackageHelper { mPm.mPackages, Collections.singletonMap(pkgName, mPm.getSettingsVersionForPackage(parsedPackage)), mSharedLibraries, mPm.mSettings.getKeySetManagerService(), - mPm.mSettings); + mPm.mSettings, mPm.mInjector.getSystemConfig()); if ((scanFlags & SCAN_AS_APEX) == 0) { appIdCreated = optimisticallyRegisterAppId(installRequest); } else { diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 9a7916a7b215..90d6adc4fa52 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY; @@ -25,6 +26,7 @@ import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; import static com.android.server.pm.PackageManagerService.TAG; +import android.content.pm.Flags; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; @@ -36,6 +38,7 @@ import android.util.Slog; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.SystemConfig; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.utils.WatchedLongSparseArray; @@ -53,14 +56,17 @@ import java.util.Map; * as install) led to the request. */ final class ReconcilePackageUtils { - private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true; + // TODO(b/308573259): with allow-list, we should be able to disallow such installs even in + // debuggable builds. + private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS = Build.IS_DEBUGGABLE + || !Flags.restrictNonpreloadsSystemShareduids(); public static List<ReconciledPackage> reconcilePackages( List<InstallRequest> installRequests, Map<String, AndroidPackage> allPackages, Map<String, Settings.VersionInfo> versionInfos, SharedLibrariesImpl sharedLibraries, - KeySetManagerService ksms, Settings settings) + KeySetManagerService ksms, Settings settings, SystemConfig systemConfig) throws ReconcileFailure { final List<ReconciledPackage> result = new ArrayList<>(installRequests.size()); @@ -187,11 +193,19 @@ final class ReconcilePackageUtils { SigningDetails.CertCapabilities.PERMISSION)) { Slog.d(TAG, "Non-preload app associated with system signature: " + signatureCheckPs.getPackageName()); - if (!ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE) { - throw new ReconcileFailure( - INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, - "Non-preload app associated with system signature: " - + signatureCheckPs.getPackageName()); + if (sharedUserSetting != null && !ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS) { + // Check the allow-list. + var allowList = systemConfig.getPackageToSharedUidAllowList(); + var sharedUidName = allowList.get(signatureCheckPs.getPackageName()); + if (sharedUidName == null + || !sharedUserSetting.name.equals(sharedUidName)) { + var msg = "Non-preload app " + signatureCheckPs.getPackageName() + + " signed with platform signature and joining shared uid: " + + sharedUserSetting.name; + Slog.e(TAG, msg + ", allowList: " + allowList); + throw new ReconcileFailure( + INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, msg); + } } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fe65010b7281..aaf21c8860f4 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4558,6 +4558,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile for (int i = 0; i < size; i++) { final PackageSetting ps = mPackages.valueAt(i); if (ps.getPkg() == null) { + // This would force-create correct per-user state. + ps.setInstalled(false, userHandle); + // Make sure the app is excluded from storage mapping for this user. + writeKernelMappingLPr(ps); continue; } final boolean shouldMaybeInstall = ps.isSystem() && diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index c1ab3f9e3eb9..211b7546bd8a 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -3795,6 +3795,7 @@ public class ShortcutService extends IShortcutService.Stub { } final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + final boolean archival = intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false); switch (action) { case Intent.ACTION_PACKAGE_ADDED: @@ -3805,7 +3806,7 @@ public class ShortcutService extends IShortcutService.Stub { } break; case Intent.ACTION_PACKAGE_REMOVED: - if (!replacing) { + if (!replacing || (replacing && archival)) { handlePackageRemoved(packageName, userId); } break; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java index 47032ea2d6af..754b141ff10d 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -408,6 +408,9 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte /** * Gets the permission states for requested package, persistent device and user. + * <p> + * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the + * exact permission states for the requested device. * * @param packageName name of the package you are checking against * @param deviceId id of the persistent device you are checking against diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java index 32a21c587f08..cebf7fb9a627 100644 --- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java @@ -21,7 +21,6 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_MEDIA_STORAGE; import static android.app.AppOpsManager.OP_LEGACY_STORAGE; import static android.app.AppOpsManager.OP_NONE; -import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; @@ -148,7 +147,7 @@ public abstract class SoftRestrictedPermissionPolicy { pkg.hasPreserveLegacyExternalStorage(); targetSDK = getMinimumTargetSDK(context, appInfo, user); - shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + shouldApplyRestriction = !isWhiteListed; isForcedScopedStorage = sForcedScopedStorageAppWhitelist .contains(appInfo.packageName); } else { diff --git a/services/core/java/com/android/server/power/Android.bp b/services/core/java/com/android/server/power/Android.bp index 607d435b5410..863ff76cb0c7 100644 --- a/services/core/java/com/android/server/power/Android.bp +++ b/services/core/java/com/android/server/power/Android.bp @@ -9,4 +9,5 @@ aconfig_declarations { java_aconfig_library { name: "backstage_power_flags_lib", aconfig_declarations: "backstage_power_flags", + sdk_version: "system_current", } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java index 601c7f450d4f..9616c286f1b3 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java @@ -39,6 +39,7 @@ import android.util.SparseArray; import android.view.DisplayInfo; import android.view.View; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.utils.TimingsTraceAndSlog; import libcore.io.IoUtils; @@ -65,7 +66,7 @@ public class WallpaperCropper { * Maximum acceptable parallax. * A value of 1 means "the additional width for parallax is at most 100% of the screen width" */ - private static final float MAX_PARALLAX = 1f; + @VisibleForTesting static final float MAX_PARALLAX = 1f; /** * We define three ways to adjust a crop. These modes are used depending on the situation: @@ -73,10 +74,9 @@ public class WallpaperCropper { * - When going from folded to unfolded, we want to add content * - For a screen rotation, we want to keep the same amount of content */ - private static final int ADD = 1; - private static final int REMOVE = 2; - private static final int BALANCE = 3; - + @VisibleForTesting static final int ADD = 1; + @VisibleForTesting static final int REMOVE = 2; + @VisibleForTesting static final int BALANCE = 3; private final WallpaperDisplayHelper mWallpaperDisplayHelper; @@ -209,7 +209,8 @@ public class WallpaperCropper { * Given a crop, a displaySize for the orientation of that crop, compute the visible part of the * crop. This removes any additional width used for parallax. No-op if displaySize == null. */ - private static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) { + @VisibleForTesting + static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) { if (displaySize == null) return crop; Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); // only keep the visible part (without parallax) @@ -240,12 +241,13 @@ public class WallpaperCropper { * </li> * </ul> */ - private static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize, + @VisibleForTesting + static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize, boolean parallax, boolean rtl, int mode) { Rect adjustedCrop = new Rect(crop); float cropRatio = ((float) crop.width()) / crop.height(); float screenRatio = ((float) screenSize.x) / screenSize.y; - if (cropRatio >= screenRatio) { + if (cropRatio > screenRatio) { if (!parallax) { // rotate everything 90 degrees clockwise, compute the result, and rotate back int newLeft = bitmapSize.y - crop.bottom; diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java index 596de686089e..1f9d265a0113 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java @@ -96,6 +96,9 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { private boolean mWebViewPackageDirty = false; private boolean mAnyWebViewInstalled = false; + // Keeps track of whether we attempted to repair WebView before. + private boolean mAttemptedToRepairBefore = false; + private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; // The WebView package currently in use (or the one we are preparing). @@ -136,6 +139,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { boolean removedOrChangedOldPackage = false; String oldProviderName = null; PackageInfo newPackage = null; + boolean repairNeeded = false; synchronized (mLock) { try { newPackage = findPreferredWebViewPackage(); @@ -161,6 +165,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { Slog.e(TAG, "Could not find valid WebView package to create relro with " + e); } + repairNeeded = shouldTriggerRepairLocked(); } if (updateWebView && !removedOrChangedOldPackage && oldProviderName != null) { @@ -170,12 +175,18 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { // only kills dependents of packages that are being removed. mSystemInterface.killPackageDependents(oldProviderName); } + if (repairNeeded) { + attemptRepair(); + } return; } } } private boolean shouldTriggerRepairLocked() { + if (mAttemptedToRepairBefore) { + return false; + } if (mCurrentWebViewPackage == null) { return true; } @@ -189,6 +200,26 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { } } + private void attemptRepair() { + // We didn't find a valid WebView implementation. Try explicitly re-installing and + // re-enabling the default package for all users in case it was disabled. If this actually + // changes the state, we will see the PackageManager broadcast shortly and try again. + synchronized (mLock) { + if (mAttemptedToRepairBefore) { + return; + } + mAttemptedToRepairBefore = true; + } + Slog.w( + TAG, + "No provider available for all users, trying to install and enable " + + mDefaultProvider.packageName); + mSystemInterface.installExistingPackageForAllUsers( + mContext, mDefaultProvider.packageName); + mSystemInterface.enablePackageForAllUsers( + mContext, mDefaultProvider.packageName, true); + } + @Override public void prepareWebViewInSystemServer() { try { @@ -211,18 +242,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { } if (repairNeeded) { - // We didn't find a valid WebView implementation. Try explicitly re-installing and - // re-enabling the default package for all users in case it was disabled, even if we - // already did the one-time migration before. If this actually changes the state, we - // will see the PackageManager broadcast shortly and try again. - Slog.w( - TAG, - "No provider available for all users, trying to install and enable " - + mDefaultProvider.packageName); - mSystemInterface.installExistingPackageForAllUsers( - mContext, mDefaultProvider.packageName); - mSystemInterface.enablePackageForAllUsers( - mContext, mDefaultProvider.packageName, true); + attemptRepair(); } } catch (Throwable t) { @@ -332,6 +352,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { PackageInfo oldPackage = null; PackageInfo newPackage = null; boolean providerChanged = false; + boolean repairNeeded = false; synchronized (mLock) { oldPackage = mCurrentWebViewPackage; @@ -354,11 +375,19 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { if (providerChanged) { onWebViewProviderChanged(newPackage); } + // Choosing another provider shouldn't break our state. Only check if repair + // is needed if this function is called as a result of a user change. + if (newProviderName == null) { + repairNeeded = shouldTriggerRepairLocked(); + } } // Kill apps using the old provider only if we changed provider if (providerChanged && oldPackage != null) { mSystemInterface.killPackageDependents(oldPackage.packageName); } + if (repairNeeded) { + attemptRepair(); + } // Return the new provider, this is not necessarily the one we were asked to switch to, // but the persistent setting will now be pointing to the provider we were asked to // switch to anyway. diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 7d5aa96db0b8..d30a2167a183 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -553,7 +553,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? - int configChangeFlags; // which config values have changed private boolean keysPaused; // has key dispatching been paused for it? int launchMode; // the launch mode activity attribute. int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override @@ -1295,10 +1294,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mDeferHidingClient) { pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient); } - if (configChangeFlags != 0) { - pw.print(prefix); pw.print(" configChangeFlags="); - pw.println(Integer.toHexString(configChangeFlags)); - } if (mServiceConnectionsHolder != null) { pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder); } @@ -4064,7 +4059,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this); mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - DestroyActivityItem.obtain(token, finishing, configChangeFlags)); + DestroyActivityItem.obtain(token, finishing)); } catch (Exception e) { // We can just ignore exceptions here... if the process has crashed, our death // notification will clean things up. @@ -4106,8 +4101,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - configChangeFlags = 0; - return removedFromHistory; } @@ -5026,7 +5019,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return PauseActivityItem.obtain(token); case STOPPING: case STOPPED: - return StopActivityItem.obtain(token, configChangeFlags); + return StopActivityItem.obtain(token); default: // Do not send a result immediately if the activity is in state INITIALIZING, // RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED. @@ -6300,7 +6293,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), PauseActivityItem.obtain(token, finishing, false /* userLeaving */, - configChangeFlags, false /* dontReport */, mAutoEnteringPip)); + false /* dontReport */, mAutoEnteringPip)); } catch (Exception e) { Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e); } @@ -6614,7 +6607,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A EventLogTags.writeWmStopActivity( mUserId, System.identityHashCode(this), shortComponentName); mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - StopActivityItem.obtain(token, configChangeFlags)); + StopActivityItem.obtain(token)); mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT); } catch (Exception e) { @@ -9858,7 +9851,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (shouldRelaunchLocked(changes, mTmpConfig)) { // Aha, the activity isn't handling the change, so DIE DIE DIE. - configChangeFlags |= changes; if (mVisible && mAtmService.mTmpUpdateConfigurationResult.mIsUpdating && !mTransitionController.isShellTransitionsEnabled()) { startFreezingScreenLocked(app, mAtmService.mTmpUpdateConfigurationResult.changes); @@ -9887,7 +9879,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_STATES, "Config is relaunching invisible " + "activity %s called by %s", this, Debug.getCallers(4)); } - relaunchActivityLocked(preserveWindow); + relaunchActivityLocked(preserveWindow, changes); // All done... tell the caller we weren't able to keep this activity around. return false; @@ -10029,9 +10021,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A | CONFIG_SCREEN_LAYOUT)) != 0; } - void relaunchActivityLocked(boolean preserveWindow) { + void relaunchActivityLocked(boolean preserveWindow, int configChangeFlags) { if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) { - configChangeFlags = 0; return; } if (!preserveWindow) { @@ -10104,8 +10095,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The activity may be waiting for stop, but that is no longer appropriate for it. mTaskSupervisor.mStoppingActivities.remove(this); - - configChangeFlags = 0; } /** @@ -10174,7 +10163,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // {@link ActivityTaskManagerService.activityStopped}). try { mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - StopActivityItem.obtain(token, 0 /* configChanges */)); + StopActivityItem.obtain(token)); } catch (RemoteException e) { Slog.w(TAG, "Exception thrown during restart " + this, e); } diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java index 64d8c7555fa6..86804360f6f4 100644 --- a/services/core/java/com/android/server/wm/SnapshotCache.java +++ b/services/core/java/com/android/server/wm/SnapshotCache.java @@ -16,7 +16,6 @@ package com.android.server.wm; import android.annotation.Nullable; -import android.hardware.HardwareBuffer; import android.util.ArrayMap; import android.window.TaskSnapshot; @@ -93,10 +92,6 @@ abstract class SnapshotCache<TYPE extends WindowContainer> { if (entry != null) { mAppIdMap.remove(entry.topApp); mRunningCache.remove(id); - final HardwareBuffer buffer = entry.snapshot.getHardwareBuffer(); - if (buffer != null) { - buffer.close(); - } } } } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 85d81c4db2ca..78ababc6473f 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1881,7 +1881,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(), PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving, - prev.configChangeFlags, pauseImmediately, autoEnteringPip)); + pauseImmediately, autoEnteringPip)); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Slog.w(TAG, "Exception thrown during pause", e); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 594043d380c9..9b19a707d7be 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -349,7 +349,7 @@ class WallpaperController { final Rect lastWallpaperBounds = wallpaperWin.getParentFrame(); int screenWidth = lastWallpaperBounds.width(); int screenHeight = lastWallpaperBounds.height(); - float screenRatio = ((float) screenWidth) / screenHeight; + float screenRatio = (float) screenWidth / screenHeight; Point screenSize = new Point(screenWidth, screenHeight); WallpaperWindowToken token = wallpaperWin.mToken.asWallpaperToken(); @@ -399,20 +399,32 @@ class WallpaperController { Point bitmapSize = new Point( wallpaperWin.mRequestedWidth, wallpaperWin.mRequestedHeight); SparseArray<Rect> cropHints = token.getCropHints(); - wallpaperFrame = mWallpaperCropUtils.getCrop( - screenSize, bitmapSize, cropHints, wallpaperWin.isRtl()); - - cropZoom = wallpaperFrame.isEmpty() ? 1f - : ((float) screenHeight) / wallpaperFrame.height() / wallpaperWin.mVScale; - - // A positive x / y offset shifts the wallpaper to the right / bottom respectively. - cropOffsetX = -wallpaperFrame.left - + (int) ((cropZoom - 1f) * wallpaperFrame.height() * screenRatio / 2f); - cropOffsetY = -wallpaperFrame.top - + (int) ((cropZoom - 1f) * wallpaperFrame.height() / 2f); - - diffWidth = (int) (wallpaperFrame.width() * wallpaperWin.mHScale) - screenWidth; - diffHeight = (int) (wallpaperFrame.height() * wallpaperWin.mVScale) - screenHeight; + wallpaperFrame = bitmapSize.x <= 0 || bitmapSize.y <= 0 ? wallpaperWin.getFrame() + : mWallpaperCropUtils.getCrop(screenSize, bitmapSize, cropHints, + wallpaperWin.isRtl()); + int frameWidth = wallpaperFrame.width(); + int frameHeight = wallpaperFrame.height(); + float frameRatio = (float) frameWidth / frameHeight; + + // If the crop is proportionally wider/taller than the screen, scale it so that its + // height/width matches the screen height/width, and use the additional width/height + // for parallax (respectively). + boolean scaleHeight = frameRatio >= screenRatio; + cropZoom = wallpaperFrame.isEmpty() ? 1f : scaleHeight + ? (float) screenHeight / frameHeight / wallpaperWin.mVScale + : (float) screenWidth / frameWidth / wallpaperWin.mHScale; + + // The dimensions of the frame, without the additional width or height for parallax. + float w = scaleHeight ? frameHeight * screenRatio : frameWidth; + float h = scaleHeight ? frameHeight : frameWidth / screenRatio; + + // Note: a positive x/y offset shifts the wallpaper to the right/bottom respectively. + cropOffsetX = -wallpaperFrame.left + (int) ((cropZoom - 1f) * w / 2f); + cropOffsetY = -wallpaperFrame.top + (int) ((cropZoom - 1f) * h / 2f); + + // Available width or height for parallax + diffWidth = (int) ((frameWidth - w) * wallpaperWin.mHScale); + diffHeight = (int) ((frameHeight - h) * wallpaperWin.mVScale); } else { wallpaperFrame = wallpaperWin.getFrame(); cropZoom = 1f; diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp index ccd9bd0a50ca..b2bdaa35f28c 100644 --- a/services/core/jni/com_android_server_hint_HintManagerService.cpp +++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp @@ -24,16 +24,17 @@ #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <powermanager/PowerHalController.h> +#include <powermanager/PowerHintSessionWrapper.h> #include <utils/Log.h> #include <unordered_map> #include "jni.h" -using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::SessionHint; using aidl::android::hardware::power::SessionMode; using aidl::android::hardware::power::WorkDuration; +using android::power::PowerHintSessionWrapper; using android::base::StringPrintf; @@ -49,7 +50,7 @@ static struct { } gWorkDurationInfo; static power::PowerHalController gPowerHalController; -static std::unordered_map<jlong, std::shared_ptr<IPowerHintSession>> gSessionMap; +static std::unordered_map<jlong, std::shared_ptr<PowerHintSessionWrapper>> gSessionMap; static std::mutex gSessionMapLock; static int64_t getHintSessionPreferredRate() { @@ -76,45 +77,45 @@ static jlong createHintSession(JNIEnv* env, int32_t tgid, int32_t uid, } static void pauseHintSession(JNIEnv* env, int64_t session_ptr) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->pause(); } static void resumeHintSession(JNIEnv* env, int64_t session_ptr) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->resume(); } static void closeHintSession(JNIEnv* env, int64_t session_ptr) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->close(); std::unique_lock<std::mutex> sessionLock(gSessionMapLock); gSessionMap.erase(session_ptr); } static void updateTargetWorkDuration(int64_t session_ptr, int64_t targetDurationNanos) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->updateTargetWorkDuration(targetDurationNanos); } static void reportActualWorkDuration(int64_t session_ptr, const std::vector<WorkDuration>& actualDurations) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->reportActualWorkDuration(actualDurations); } static void sendHint(int64_t session_ptr, SessionHint hint) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->sendHint(hint); } static void setThreads(int64_t session_ptr, const std::vector<int32_t>& threadIds) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->setThreads(threadIds); } static void setMode(int64_t session_ptr, SessionMode mode, bool enabled) { - auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr); + auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr); appSession->setMode(mode, enabled); } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java index 027337f04b6e..f5e1e41dbae4 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java @@ -113,8 +113,8 @@ public class CredentialManagerUi { /** Creates intent that is ot be invoked to cancel an in-progress UI session. */ public Intent createCancelIntent(IBinder requestId, String packageName) { - return IntentFactory.createCancelUiIntent(requestId, /*shouldShowCancellationUi=*/ true, - packageName); + return IntentFactory.createCancelUiIntent(mContext, requestId, + /*shouldShowCancellationUi=*/ true, packageName); } /** diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java index 3cb98eb4cd7a..eff53de75ff4 100644 --- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java @@ -41,6 +41,8 @@ import android.service.credentials.CredentialProviderService; import android.service.credentials.PermissionUtils; import android.util.Slog; +import com.android.server.credentials.metrics.ApiStatus; + import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -180,7 +182,7 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ } else { Slog.w(TAG, "onUiCancellation called but finalResponseReceiver not found"); } - finishSession(/*propagateCancellation=*/false); + finishSession(/*propagateCancellation=*/false, ApiStatus.FAILURE.getMetricCode()); } @Override @@ -221,9 +223,10 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ resultData.putParcelable( CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response); mFinalResponseReceiver.send(Constants.SUCCESS_CREDMAN_SELECTOR, resultData); - finishSession(/*propagateCancellation=*/ false); + finishSession(/*propagateCancellation=*/ false, ApiStatus.SUCCESS.getMetricCode()); } else { Slog.w(TAG, "onFinalResponseReceived result receiver not found for pinned entry"); + finishSession(/*propagateCancellation=*/ false, ApiStatus.FAILURE.getMetricCode()); } } diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index 633c9c4cb8e1..a5b9aa68b22e 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -175,7 +175,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential () -> { Slog.d(TAG, "Cancellation invoked from the client - clearing session"); boolean isUiActive = maybeCancelUi(); - finishSession(!isUiActive); + finishSession(!isUiActive, ApiStatus.CLIENT_CANCELED.getMetricCode()); } ); } @@ -231,7 +231,8 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential return; } if (isSessionCancelled()) { - finishSession(/*propagateCancellation=*/true); + finishSession(/*propagateCancellation=*/true, + ApiStatus.CLIENT_CANCELED.getMetricCode()); return; } String providerId = selection.getProviderId(); @@ -257,11 +258,12 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential } } - protected void finishSession(boolean propagateCancellation) { + protected void finishSession(boolean propagateCancellation, int apiStatus) { Slog.i(TAG, "finishing session with propagateCancellation " + propagateCancellation); if (propagateCancellation) { mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession); } + mRequestSessionMetric.logApiCalledAtFinish(apiStatus); mRequestSessionStatus = RequestSessionStatus.COMPLETE; mProviders.clear(); clearRequestSessionLocked(); @@ -326,7 +328,8 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential mRequestSessionMetric.logCandidatePhaseMetrics(mProviders); if (isSessionCancelled()) { - finishSession(/*propagateCancellation=*/true); + finishSession(/*propagateCancellation=*/true, + ApiStatus.CLIENT_CANCELED.getMetricCode()); return providerDataList; } @@ -353,23 +356,20 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential return; } if (isSessionCancelled()) { - mRequestSessionMetric.logApiCalledAtFinish( - /*apiStatus=*/ ApiStatus.CLIENT_CANCELED.getMetricCode()); - finishSession(/*propagateCancellation=*/true); + finishSession(/*propagateCancellation=*/true, + ApiStatus.CLIENT_CANCELED.getMetricCode()); return; } try { invokeClientCallbackSuccess(response); - mRequestSessionMetric.logApiCalledAtFinish( - /*apiStatus=*/ ApiStatus.SUCCESS.getMetricCode()); + finishSession(/*propagateCancellation=*/false, + ApiStatus.SUCCESS.getMetricCode()); } catch (RemoteException e) { mRequestSessionMetric.collectFinalPhaseProviderMetricStatus( /*has_exception=*/ true, ProviderStatusForMetrics.FINAL_FAILURE); Slog.e(TAG, "Issue while responding to client with a response : " + e); - mRequestSessionMetric.logApiCalledAtFinish( - /*apiStatus=*/ ApiStatus.FAILURE.getMetricCode()); + finishSession(/*propagateCancellation=*/false, ApiStatus.FAILURE.getMetricCode()); } - finishSession(/*propagateCancellation=*/false); } /** @@ -387,9 +387,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential return; } if (isSessionCancelled()) { - mRequestSessionMetric.logApiCalledAtFinish( - /*apiStatus=*/ ApiStatus.CLIENT_CANCELED.getMetricCode()); - finishSession(/*propagateCancellation=*/true); + finishSession(/*propagateCancellation=*/true, ApiStatus.CLIENT_CANCELED.getMetricCode()); return; } @@ -399,8 +397,14 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential Slog.e(TAG, "Issue while responding to client with error : " + e); } boolean isUserCanceled = errorType.contains(MetricUtilities.USER_CANCELED_SUBSTRING); - mRequestSessionMetric.logFailureOrUserCancel(isUserCanceled); - finishSession(/*propagateCancellation=*/false); + if (isUserCanceled) { + mRequestSessionMetric.setHasExceptionFinalPhase(/* has_exception */ false); + finishSession(/*propagateCancellation=*/false, + ApiStatus.USER_CANCELED.getMetricCode()); + } else { + finishSession(/*propagateCancellation=*/false, + ApiStatus.FAILURE.getMetricCode()); + } } /** @@ -419,7 +423,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential @Override public void binderDied() { Slog.d(TAG, "Client binder died - clearing session"); - finishSession(isUiWaitingForData()); + finishSession(isUiWaitingForData(), ApiStatus.CLIENT_CANCELED.getMetricCode()); } } } diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java index 8adcfbcb974c..a77bd3e280dd 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -247,7 +247,7 @@ public class RequestSessionMetric { * * @param exceptionBitFinalPhase represents if the final phase provider had an exception */ - private void setHasExceptionFinalPhase(boolean exceptionBitFinalPhase) { + public void setHasExceptionFinalPhase(boolean exceptionBitFinalPhase) { try { mChosenProviderFinalPhaseMetric.setHasException(exceptionBitFinalPhase); } catch (Exception e) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 12f44074a4ad..6aeb4fd53905 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -225,7 +225,7 @@ final class DevicePolicyEngine { synchronized (mLock) { PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); - if (Flags.devicePolicySizeTrackingEnabled() && false) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value, policyDefinition, userId)) { return; @@ -350,7 +350,7 @@ final class DevicePolicyEngine { } PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); - if (Flags.devicePolicySizeTrackingEnabled() && false) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin); } @@ -496,7 +496,7 @@ final class DevicePolicyEngine { synchronized (mLock) { PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); - if (Flags.devicePolicySizeTrackingEnabled() && false) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value, policyDefinition, UserHandle.USER_ALL)) { return; @@ -568,7 +568,7 @@ final class DevicePolicyEngine { synchronized (mLock) { PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition); - if (Flags.devicePolicySizeTrackingEnabled() && false) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { decreasePolicySizeForAdmin(policyState, enforcingAdmin); } @@ -1892,7 +1892,7 @@ final class DevicePolicyEngine { private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer) throws IOException { - if (Flags.devicePolicySizeTrackingEnabled() && false) { + if (Flags.devicePolicySizeTrackingInternalEnabled()) { if (mAdminPolicySize != null) { for (int i = 0; i < mAdminPolicySize.size(); i++) { int userId = mAdminPolicySize.keyAt(i); @@ -1916,7 +1916,7 @@ final class DevicePolicyEngine { private void writeMaxPolicySizeInner(TypedXmlSerializer serializer) throws IOException { - if (!Flags.devicePolicySizeTrackingEnabled() || true) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { return; } serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT); @@ -2081,7 +2081,7 @@ final class DevicePolicyEngine { private void readMaxPolicySizeInner(TypedXmlPullParser parser) throws XmlPullParserException, IOException { - if (!Flags.devicePolicySizeTrackingEnabled() || true) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { return; } mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 0f97f4a7cdc0..80046b6075ee 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -27,6 +27,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECU import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUTOFILL; @@ -83,7 +84,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER; -import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_VPN; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WALLPAPER; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI; @@ -196,11 +196,11 @@ import static android.app.admin.DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PR import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED; import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER; import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED; +import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER; import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED; import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED; import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS; import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER; -import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER; import static android.app.admin.DevicePolicyManager.STATUS_OK; import static android.app.admin.DevicePolicyManager.STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS; import static android.app.admin.DevicePolicyManager.STATUS_SYSTEM_USER; @@ -12062,7 +12062,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (packageList != null) { - if (!Flags.devicePolicySizeTrackingEnabled()) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { for (String pkg : packageList) { PolicySizeVerifier.enforceMaxPackageNameLength(pkg); } @@ -13771,7 +13771,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } - if (!Flags.devicePolicySizeTrackingEnabled()) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { PolicySizeVerifier.enforceMaxStringLength(accountType, "account type"); } @@ -14385,7 +14385,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages) throws SecurityException { Objects.requireNonNull(packages, "packages is null"); - if (!Flags.devicePolicySizeTrackingEnabled()) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { for (String pkg : packages) { PolicySizeVerifier.enforceMaxPackageNameLength(pkg); } @@ -24235,7 +24235,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) { - if (!Flags.devicePolicySizeTrackingEnabled() || true) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { return; } CallerIdentity caller = getCallerIdentity(callerPackageName); @@ -24247,7 +24247,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public int getMaxPolicyStorageLimit(String callerPackageName) { - if (!Flags.devicePolicySizeTrackingEnabled() || true) { + if (!Flags.devicePolicySizeTrackingInternalEnabled()) { return -1; } CallerIdentity caller = getCallerIdentity(callerPackageName); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e19f08cb04a1..9d95c5b4f649 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2774,9 +2774,12 @@ public final class SystemServer implements Dumpable { t.traceEnd(); // OnDevicePersonalizationSystemService - t.traceBegin("StartOnDevicePersonalizationSystemService"); - mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS); - t.traceEnd(); + if (!com.android.server.flags.Flags.enableOdpFeatureGuard() + || SystemProperties.getBoolean("ro.system_settings.service.odp_enabled", true)) { + t.traceBegin("StartOnDevicePersonalizationSystemService"); + mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS); + t.traceEnd(); + } // Profiling if (android.server.Flags.telemetryApisService()) { diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt index fc2eb2652d2d..c0d988d0c46b 100644 --- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt +++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt @@ -20,27 +20,63 @@ import android.app.AppOpsManager import android.companion.virtual.VirtualDeviceManager import android.os.Handler import android.os.UserHandle +import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArraySet +import android.util.LongSparseArray +import android.util.Slog +import android.util.SparseArray import android.util.SparseBooleanArray import android.util.SparseIntArray import com.android.internal.annotations.VisibleForTesting +import com.android.internal.util.IntPair import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.AppOpUri +import com.android.server.permission.access.DevicePermissionUri +import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.PackageUri +import com.android.server.permission.access.PermissionUri import com.android.server.permission.access.UidUri +import com.android.server.permission.access.appop.AppOpModes.MODE_ALLOWED +import com.android.server.permission.access.appop.AppOpModes.MODE_FOREGROUND +import com.android.server.permission.access.appop.AppOpModes.MODE_IGNORED import com.android.server.permission.access.collection.forEachIndexed import com.android.server.permission.access.collection.set +import com.android.server.permission.access.permission.AppIdPermissionPolicy +import com.android.server.permission.access.permission.DevicePermissionPolicy +import com.android.server.permission.access.permission.PermissionFlags +import com.android.server.permission.access.permission.PermissionService class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface { private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy + private val permissionPolicy = + service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy + private val devicePermissionPolicy = + service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy private val context = service.context + + // Maps appop code to its runtime permission + private val runtimeAppOpToPermissionNames = SparseArray<String>() + + // Maps runtime permission to its appop codes + private val runtimePermissionNameToAppOp = ArrayMap<String, Int>() + + private var foregroundableOps = SparseBooleanArray() + + /* Maps foreground permissions to their background permission. Background permissions aren't + required to be runtime */ + private val foregroundToBackgroundPermissionName = ArrayMap<String, String>() + + /* Maps background permissions to their foreground permissions. Background permissions aren't + required to be runtime */ + private val backgroundToForegroundPermissionNames = ArrayMap<String, ArraySet<String>>() + private lateinit var handler: Handler @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() @@ -69,11 +105,60 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } override fun systemReady() { - // Not implemented because upgrades are handled automatically. + if (Flags.runtimePermissionAppopsMappingEnabled()) { + createPermissionAppOpMapping() + val permissionListener = OnPermissionFlagsChangedListener() + permissionPolicy.addOnPermissionFlagsChangedListener(permissionListener) + devicePermissionPolicy.addOnPermissionFlagsChangedListener(permissionListener) + } + } + + private fun createPermissionAppOpMapping() { + val permissions = service.getState { with(permissionPolicy) { getPermissions() } } + + for (appOpCode in 0 until AppOpsManager._NUM_OP) { + AppOpsManager.opToPermission(appOpCode)?.let { permissionName -> + // Multiple ops might map to a single permission but only one is considered the + // runtime appop calculations. + if (appOpCode == AppOpsManager.permissionToOpCode(permissionName)) { + val permission = permissions[permissionName]!! + if (permission.isRuntime) { + runtimePermissionNameToAppOp[permissionName] = appOpCode + runtimeAppOpToPermissionNames[appOpCode] = permissionName + permission.permissionInfo.backgroundPermission?.let { + backgroundPermissionName -> + // Note: background permission may not be runtime, + // e.g. microphone/camera. + foregroundableOps[appOpCode] = true + foregroundToBackgroundPermissionName[permissionName] = + backgroundPermissionName + backgroundToForegroundPermissionNames + .getOrPut(backgroundPermissionName, ::ArraySet) + .add(permissionName) + } + } + } + } + } } override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { - return opNameMapToOpSparseArray(getUidModes(uid)) + val appId = UserHandle.getAppId(uid) + val userId = UserHandle.getUserId(uid) + service.getState { + val modes = + with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) } + if (Flags.runtimePermissionAppopsMappingEnabled()) { + runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode -> + val mode = getUidModeFromPermissionState(appId, userId, permissionName) + if (mode != AppOpsManager.opToDefaultMode(appOpCode)) { + modes[appOpCode] = mode + } + } + } + + return modes + } } override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray { @@ -84,7 +169,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) - return service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } + val permissionName = runtimeAppOpToPermissionNames[op] + + return if (!Flags.runtimePermissionAppopsMappingEnabled() || permissionName == null) { + service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } + } else { + service.getState { getUidModeFromPermissionState(appId, userId, permissionName) } + } } private fun getUidModes(uid: Int): ArrayMap<String, Int>? { @@ -93,13 +184,66 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map } - override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean { + private fun GetStateScope.getUidModeFromPermissionState( + appId: Int, + userId: Int, + permissionName: String + ): Int { + val permissionFlags = + with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } + val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName] + val backgroundPermissionFlags = + if (backgroundPermissionName != null) { + with(permissionPolicy) { + getPermissionFlags(appId, userId, backgroundPermissionName) + } + } else { + PermissionFlags.RUNTIME_GRANTED + } + val result = evaluateModeFromPermissionFlags(permissionFlags, backgroundPermissionFlags) + if (result != MODE_IGNORED) { + return result + } + + val fullerPermissionName = + PermissionService.getFullerPermission(permissionName) ?: return result + return getUidModeFromPermissionState(appId, userId, fullerPermissionName) + } + + private fun evaluateModeFromPermissionFlags( + foregroundFlags: Int, + backgroundFlags: Int = PermissionFlags.RUNTIME_GRANTED + ): Int = + if (PermissionFlags.isAppOpGranted(foregroundFlags)) { + if (PermissionFlags.isAppOpGranted(backgroundFlags)) { + MODE_ALLOWED + } else { + MODE_FOREGROUND + } + } else { + MODE_IGNORED + } + + override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean { + if ( + Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames + ) { + Slog.w( + LOG_TAG, + "Cannot set UID mode for runtime permission app op, uid = $uid," + + " code = ${AppOpsManager.opToName(code)}," + + " mode = ${AppOpsManager.modeToName(mode)}", + RuntimeException() + ) + return false + } + val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) - val opName = AppOpsManager.opToPublicName(op) - var wasChanged = false + val appOpName = AppOpsManager.opToPublicName(code) + var wasChanged: Boolean service.mutateState { - wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) } + wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) } } return wasChanged } @@ -114,10 +258,23 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map - override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) { - val opName = AppOpsManager.opToPublicName(op) + override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) { + val appOpName = AppOpsManager.opToPublicName(appOpCode) + + if ( + Flags.runtimePermissionAppopsMappingEnabled() && + appOpCode in runtimeAppOpToPermissionNames + ) { + Slog.w( + LOG_TAG, + "(packageName=$packageName, userId=$userId)'s appop state" + + " for runtime op $appOpName should not be set directly.", + RuntimeException() + ) + return + } service.mutateState { - with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) } + with(packagePolicy) { setAppOpMode(packageName, userId, appOpName, mode) } } } @@ -128,7 +285,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } override fun removePackage(packageName: String, userId: Int): Boolean { - var wasChanged = false + var wasChanged: Boolean service.mutateState { wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } } @@ -158,6 +315,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS this[AppOpsManager.strOpToOp(op)] = true } } + if (Flags.runtimePermissionAppopsMappingEnabled()) { + foregroundableOps.forEachIndexed { _, op, _ -> + if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) { + this[op] = true + } + } + } } } @@ -168,6 +332,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS this[AppOpsManager.strOpToOp(op)] = true } } + if (Flags.runtimePermissionAppopsMappingEnabled()) { + foregroundableOps.forEachIndexed { _, op, _ -> + if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) { + this[op] = true + } + } + } } } @@ -189,9 +360,10 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } - inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() { + private inner class OnAppIdAppOpModeChangedListener : + AppIdAppOpPolicy.OnAppOpModeChangedListener() { // (uid, appOpCode) -> newMode - val pendingChanges = ArrayMap<Pair<Int, Int>, Int>() + private val pendingChanges = LongSparseArray<Int>() override fun onAppOpModeChanged( appId: Int, @@ -202,7 +374,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS ) { val uid = UserHandle.getUid(userId, appId) val appOpCode = AppOpsManager.strOpToOp(appOpName) - val key = Pair(uid, appOpCode) + val key = IntPair.of(uid, appOpCode) pendingChanges[key] = newMode } @@ -211,13 +383,15 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> - val uid = key.first - val appOpCode = key.second + val uid = IntPair.first(key) + val appOpCode = IntPair.second(key) - listener.onUidModeChanged(uid, + listener.onUidModeChanged( + uid, appOpCode, mode, - VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT + ) } } @@ -228,7 +402,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private inner class OnPackageAppOpModeChangedListener : PackageAppOpPolicy.OnAppOpModeChangedListener() { // (packageName, userId, appOpCode) -> newMode - val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() + private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() override fun onAppOpModeChanged( packageName: String, @@ -258,4 +432,130 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS pendingChanges.clear() } } + + private inner class OnPermissionFlagsChangedListener : + AppIdPermissionPolicy.OnPermissionFlagsChangedListener, + DevicePermissionPolicy.OnDevicePermissionFlagsChangedListener { + // (uid, deviceId, appOpCode) -> newMode + private val pendingChanges = ArrayMap<Triple<Int, String, Int>, Int>() + + override fun onPermissionFlagsChanged( + appId: Int, + userId: Int, + permissionName: String, + oldFlags: Int, + newFlags: Int + ) { + onDevicePermissionFlagsChanged( + appId, + userId, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, + permissionName, + oldFlags, + newFlags + ) + } + + override fun onDevicePermissionFlagsChanged( + appId: Int, + userId: Int, + deviceId: String, + permissionName: String, + oldFlags: Int, + newFlags: Int + ) { + backgroundToForegroundPermissionNames[permissionName]?.let { foregroundPermissions -> + // This is a background permission; there may be multiple foreground permissions + // affected. + foregroundPermissions.forEachIndexed { _, foregroundPermissionName -> + runtimePermissionNameToAppOp[foregroundPermissionName]?.let { appOpCode -> + val foregroundPermissionFlags = + getPermissionFlags(appId, userId, foregroundPermissionName) + addPendingChangedModeIfNeeded( + appId, + userId, + deviceId, + appOpCode, + foregroundPermissionFlags, + oldFlags, + foregroundPermissionFlags, + newFlags + ) + } + } + } + ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission + -> + runtimePermissionNameToAppOp[permissionName]?.let { appOpCode -> + val backgroundPermissionFlags = + getPermissionFlags(appId, userId, backgroundPermission) + addPendingChangedModeIfNeeded( + appId, + userId, + deviceId, + appOpCode, + oldFlags, + backgroundPermissionFlags, + newFlags, + backgroundPermissionFlags + ) + } + } + ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode -> + addPendingChangedModeIfNeeded( + appId, + userId, + deviceId, + appOpCode, + oldFlags, + PermissionFlags.RUNTIME_GRANTED, + newFlags, + PermissionFlags.RUNTIME_GRANTED + ) + } + } + + private fun getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int = + service.getState { + with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } + } + + private fun addPendingChangedModeIfNeeded( + appId: Int, + userId: Int, + deviceId: String, + appOpCode: Int, + oldForegroundFlags: Int, + oldBackgroundFlags: Int, + newForegroundFlags: Int, + newBackgroundFlags: Int, + ) { + val oldMode = evaluateModeFromPermissionFlags(oldForegroundFlags, oldBackgroundFlags) + val newMode = evaluateModeFromPermissionFlags(newForegroundFlags, newBackgroundFlags) + + if (oldMode != newMode) { + val uid = UserHandle.getUid(userId, appId) + pendingChanges[Triple(uid, deviceId, appOpCode)] = newMode + } + } + + override fun onStateMutated() { + val listenersLocal = listeners + pendingChanges.forEachIndexed { _, key, mode -> + listenersLocal.forEachIndexed { _, listener -> + val uid = key.first + val deviceId = key.second + val appOpCode = key.third + + listener.onUidModeChanged(uid, appOpCode, mode, deviceId) + } + } + + pendingChanges.clear() + } + } + + companion object { + private val LOG_TAG = AppOpService::class.java.simpleName + } } diff --git a/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt new file mode 100644 index 000000000000..827dd0e5d292 --- /dev/null +++ b/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.permission.access.collection + +import android.util.LongSparseArray + +inline fun <T> LongSparseArray<T>.allIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (!predicate(index, key, value)) { + return false + } + } + return true +} + +inline fun <T> LongSparseArray<T>.anyIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (predicate(index, key, value)) { + return true + } + } + return false +} + +inline fun <T> LongSparseArray<T>.forEachIndexed(action: (Int, Long, T) -> Unit) { + for (index in 0 until size) { + action(index, keyAt(index), valueAt(index)) + } +} + +inline fun <T> LongSparseArray<T>.forEachReversedIndexed(action: (Int, Long, T) -> Unit) { + for (index in lastIndex downTo 0) { + action(index, keyAt(index), valueAt(index)) + } +} + +inline fun <T> LongSparseArray<T>.getOrPut(key: Long, defaultValue: () -> T): T { + val index = indexOfKey(key) + return if (index >= 0) { + valueAt(index) + } else { + defaultValue().also { put(key, it) } + } +} + +inline val <T> LongSparseArray<T>.lastIndex: Int + get() = size - 1 + +@Suppress("NOTHING_TO_INLINE") +inline operator fun <T> LongSparseArray<T>.minusAssign(key: Long) { + delete(key) +} + +inline fun <T> LongSparseArray<T>.noneIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (predicate(index, key, value)) { + return false + } + } + return true +} + +inline fun <T> LongSparseArray<T>.removeAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { + var isChanged = false + forEachReversedIndexed { index, key, value -> + if (predicate(index, key, value)) { + removeAt(index) + isChanged = true + } + } + return isChanged +} + +inline fun <T> LongSparseArray<T>.retainAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { + var isChanged = false + forEachReversedIndexed { index, key, value -> + if (!predicate(index, key, value)) { + removeAt(index) + isChanged = true + } + } + return isChanged +} + +inline val <T> LongSparseArray<T>.size: Int + get() = size() + +@Suppress("NOTHING_TO_INLINE") +inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) { + put(key, value) +} diff --git a/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt new file mode 100644 index 000000000000..a582431aa83c --- /dev/null +++ b/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.permission.access.collection + +import android.util.SparseIntArray + +inline fun SparseIntArray.allIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (!predicate(index, key, value)) { + return false + } + } + return true +} + +inline fun SparseIntArray.anyIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (predicate(index, key, value)) { + return true + } + } + return false +} + +inline fun SparseIntArray.forEachIndexed(action: (Int, Int, Int) -> Unit) { + for (index in 0 until size) { + action(index, keyAt(index), valueAt(index)) + } +} + +inline fun SparseIntArray.forEachReversedIndexed(action: (Int, Int, Int) -> Unit) { + for (index in lastIndex downTo 0) { + action(index, keyAt(index), valueAt(index)) + } +} + +inline fun SparseIntArray.getOrPut(key: Int, defaultValue: () -> Int): Int { + val index = indexOfKey(key) + return if (index >= 0) { + valueAt(index) + } else { + defaultValue().also { put(key, it) } + } +} + +inline val SparseIntArray.lastIndex: Int + get() = size - 1 + +@Suppress("NOTHING_TO_INLINE") +inline operator fun SparseIntArray.minusAssign(key: Int) { + delete(key) +} + +inline fun SparseIntArray.noneIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (predicate(index, key, value)) { + return false + } + } + return true +} + +fun SparseIntArray.remove(key: Int) { + delete(key) +} + +fun SparseIntArray.remove(key: Int, defaultValue: Int): Int { + val index = indexOfKey(key) + return if (index >= 0) { + val oldValue = valueAt(index) + removeAt(index) + oldValue + } else { + defaultValue + } +} + +inline fun SparseIntArray.removeAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean { + var isChanged = false + forEachReversedIndexed { index, key, value -> + if (predicate(index, key, value)) { + removeAt(index) + isChanged = true + } + } + return isChanged +} + +inline fun SparseIntArray.retainAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean { + var isChanged = false + forEachReversedIndexed { index, key, value -> + if (!predicate(index, key, value)) { + removeAt(index) + isChanged = true + } + } + return isChanged +} + +@Suppress("NOTHING_TO_INLINE") +inline operator fun SparseIntArray.set(key: Int, value: Int) { + put(key, value) +} + +inline val SparseIntArray.size: Int + get() = size() diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index 4b086b3aca17..67df67fdf6c1 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -227,25 +227,59 @@ class AppIdPermissionPolicy : SchemePolicy() { if (isRequestedBySystemPackage) { return@forEach } - val oldFlags = getPermissionFlags(appId, userId, permissionName) - var newFlags = oldFlags andInv PermissionFlags.UPGRADE_EXEMPT - val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT) - newFlags = - if (permission.isHardRestricted && !isExempt) { - newFlags or PermissionFlags.RESTRICTION_REVOKED - } else { - newFlags andInv PermissionFlags.RESTRICTION_REVOKED - } - newFlags = - if (permission.isSoftRestricted && !isExempt) { - newFlags or PermissionFlags.SOFT_RESTRICTED - } else { - newFlags andInv PermissionFlags.SOFT_RESTRICTED - } - setPermissionFlags(appId, userId, permissionName, newFlags) + updatePermissionExemptFlags( + appId, + userId, + permission, + PermissionFlags.UPGRADE_EXEMPT, + 0 + ) } } + fun MutateStateScope.updatePermissionExemptFlags( + appId: Int, + userId: Int, + permission: Permission, + exemptFlagMask: Int, + exemptFlagValues: Int + ) { + val permissionName = permission.name + val oldFlags = getPermissionFlags(appId, userId, permissionName) + var newFlags = (oldFlags andInv exemptFlagMask) or (exemptFlagValues and exemptFlagMask) + if (oldFlags == newFlags) { + return + } + val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT) + if (permission.isHardRestricted && !isExempt) { + newFlags = newFlags or PermissionFlags.RESTRICTION_REVOKED + // If the permission was policy fixed as granted but it is no longer on any of the + // allowlists we need to clear the policy fixed flag as allowlisting trumps policy i.e. + // policy cannot grant a non grantable permission. + if (PermissionFlags.isPermissionGranted(oldFlags)) { + newFlags = newFlags andInv PermissionFlags.POLICY_FIXED + } + } else { + newFlags = newFlags andInv PermissionFlags.RESTRICTION_REVOKED + } + newFlags = + if ( + permission.isSoftRestricted && !isExempt && + !anyPackageInAppId(appId) { + permissionName in it.androidPackage!!.requestedPermissions && + isSoftRestrictedPermissionExemptForPackage(it, permissionName) + } + ) { + newFlags or PermissionFlags.SOFT_RESTRICTED + } else { + newFlags andInv PermissionFlags.SOFT_RESTRICTED + } + if (oldFlags == newFlags) { + return + } + setPermissionFlags(appId, userId, permissionName, newFlags) + } + override fun MutateStateScope.onPackageUninstalled( packageName: String, appId: Int, @@ -1118,7 +1152,12 @@ class AppIdPermissionPolicy : SchemePolicy() { newFlags andInv PermissionFlags.RESTRICTION_REVOKED } newFlags = - if (permission.isSoftRestricted && !isExempt) { + if ( + permission.isSoftRestricted && !isExempt && + !requestingPackageStates.anyIndexed { _, it -> + isSoftRestrictedPermissionExemptForPackage(it, permissionName) + } + ) { newFlags or PermissionFlags.SOFT_RESTRICTED } else { newFlags andInv PermissionFlags.SOFT_RESTRICTED @@ -1398,6 +1437,17 @@ class AppIdPermissionPolicy : SchemePolicy() { } } + // See also SoftRestrictedPermissionPolicy.mayGrantPermission() + private fun isSoftRestrictedPermissionExemptForPackage( + packageState: PackageState, + permissionName: String + ): Boolean = + when (permissionName) { + Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE -> + packageState.androidPackage!!.targetSdkVersion >= Build.VERSION_CODES.Q + else -> false + } + private inline fun MutateStateScope.anyPackageInAppId( appId: Int, state: AccessState = newState, diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt index 28889de4bbb1..c5c921dc3515 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt @@ -346,9 +346,18 @@ object PermissionFlags { return flags.hasBits(RUNTIME_GRANTED) } - fun isAppOpGranted(flags: Int): Boolean = - isPermissionGranted(flags) && !flags.hasBits(RESTRICTION_REVOKED) && - !flags.hasBits(APP_OP_REVOKED) + fun isAppOpGranted(flags: Int): Boolean { + if (!isPermissionGranted(flags)) { + return false + } + if (flags.hasAnyBit(MASK_RESTRICTED)) { + return false + } + if (flags.hasBits(APP_OP_REVOKED)) { + return false + } + return true + } fun toApiFlags(flags: Int): Int { var apiFlags = 0 diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 0704c8ffca25..0b58543e076d 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -88,7 +88,6 @@ import com.android.server.pm.PackageInstallerService import com.android.server.pm.PackageManagerLocal import com.android.server.pm.UserManagerInternal import com.android.server.pm.UserManagerService -import com.android.server.pm.parsing.pkg.AndroidPackageUtils import com.android.server.pm.permission.LegacyPermission import com.android.server.pm.permission.LegacyPermissionSettings import com.android.server.pm.permission.LegacyPermissionState @@ -97,7 +96,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInterface import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.PackageState -import com.android.server.policy.SoftRestrictedPermissionPolicy import java.io.FileDescriptor import java.io.PrintWriter import java.util.concurrent.CompletableFuture @@ -1006,25 +1004,14 @@ class PermissionService(private val service: AccessCheckingService) : } if (isGranted && oldFlags.hasBits(PermissionFlags.SOFT_RESTRICTED)) { - // TODO: Refactor SoftRestrictedPermissionPolicy. - val softRestrictedPermissionPolicy = - SoftRestrictedPermissionPolicy.forPermission( - context, - AndroidPackageUtils.generateAppInfoWithoutState(androidPackage), - androidPackage, - UserHandle.of(userId), - permissionName + if (reportError) { + Slog.e( + LOG_TAG, + "$methodName: Cannot grant soft-restricted non-exempt permission" + + " $permissionName to package $packageName" ) - if (!softRestrictedPermissionPolicy.mayGrantPermission()) { - if (reportError) { - Slog.e( - LOG_TAG, - "$methodName: Cannot grant soft-restricted non-exempt permission" + - " $permissionName to package $packageName" - ) - } - return } + return } val newFlags = PermissionFlags.updateRuntimePermissionGranted(oldFlags, isGranted) @@ -1135,25 +1122,23 @@ class PermissionService(private val service: AccessCheckingService) : return emptyMap() } - val permissionFlagsMap = - service.getState { + service.getState { + val permissionFlags = if (deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) { with(policy) { getAllPermissionFlags(packageState.appId, userId) } } else { with(devicePolicy) { getAllPermissionFlags(packageState.appId, deviceId, userId) } - } + } ?: return emptyMap() + val permissionStates = ArrayMap<String, PermissionState>() + permissionFlags.forEachIndexed { _, permissionName, flags -> + val granted = isPermissionGranted(packageState, userId, permissionName, deviceId) + val apiFlags = PermissionFlags.toApiFlags(flags) + permissionStates[permissionName] = PermissionState(granted, apiFlags) } - ?: return emptyMap() - - val permissionStates = ArrayMap<String, PermissionState>() - permissionFlagsMap.forEachIndexed { _, permissionName, flags -> - val granted = PermissionFlags.isPermissionGranted(flags) - val apiFlags = PermissionFlags.toApiFlags(flags) - permissionStates[permissionName] = PermissionState(granted, apiFlags) + return permissionStates } - return permissionStates } override fun isPermissionRevokedByPolicy( @@ -1852,10 +1837,19 @@ class PermissionService(private val service: AccessCheckingService) : allowlistedFlags: Int, userId: Int ) { + var exemptMask = 0 + if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) { + exemptMask = exemptMask or PermissionFlags.SYSTEM_EXEMPT + } + if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) { + exemptMask = exemptMask or PermissionFlags.UPGRADE_EXEMPT + } + if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) { + exemptMask = exemptMask or PermissionFlags.INSTALLER_EXEMPT + } + service.mutateState { with(policy) { - val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState - val permissions = getPermissions() androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission -> val permission = permissions[requestedPermission] @@ -1863,81 +1857,8 @@ class PermissionService(private val service: AccessCheckingService) : return@forEachIndexed } - val oldFlags = permissionsFlags[requestedPermission] ?: 0 - val wasGranted = PermissionFlags.isPermissionGranted(oldFlags) - - var newFlags = oldFlags - var mask = 0 - var allowlistFlagsCopy = allowlistedFlags - while (allowlistFlagsCopy != 0) { - val flag = 1 shl allowlistFlagsCopy.countTrailingZeroBits() - allowlistFlagsCopy = allowlistFlagsCopy and flag.inv() - when (flag) { - PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM -> { - mask = mask or PermissionFlags.SYSTEM_EXEMPT - newFlags = - if (permissionNames.contains(requestedPermission)) { - newFlags or PermissionFlags.SYSTEM_EXEMPT - } else { - newFlags andInv PermissionFlags.SYSTEM_EXEMPT - } - } - PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE -> { - mask = mask or PermissionFlags.UPGRADE_EXEMPT - newFlags = - if (permissionNames.contains(requestedPermission)) { - newFlags or PermissionFlags.UPGRADE_EXEMPT - } else { - newFlags andInv PermissionFlags.UPGRADE_EXEMPT - } - } - PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER -> { - mask = mask or PermissionFlags.INSTALLER_EXEMPT - newFlags = - if (permissionNames.contains(requestedPermission)) { - newFlags or PermissionFlags.INSTALLER_EXEMPT - } else { - newFlags andInv PermissionFlags.INSTALLER_EXEMPT - } - } - } - } - - if (oldFlags == newFlags) { - return@forEachIndexed - } - - val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT) - - // If the permission is policy fixed as granted but it is no longer - // on any of the allowlists we need to clear the policy fixed flag - // as allowlisting trumps policy i.e. policy cannot grant a non - // grantable permission. - if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED)) { - if (!isExempt && wasGranted) { - mask = mask or PermissionFlags.POLICY_FIXED - newFlags = newFlags andInv PermissionFlags.POLICY_FIXED - } - } - - newFlags = - if (permission.isHardRestricted && !isExempt) { - newFlags or PermissionFlags.RESTRICTION_REVOKED - } else { - newFlags andInv PermissionFlags.RESTRICTION_REVOKED - } - newFlags = - if (permission.isSoftRestricted && !isExempt) { - newFlags or PermissionFlags.SOFT_RESTRICTED - } else { - newFlags andInv PermissionFlags.SOFT_RESTRICTED - } - mask = - mask or - PermissionFlags.RESTRICTION_REVOKED or - PermissionFlags.SOFT_RESTRICTED - - updatePermissionFlags(appId, userId, requestedPermission, mask, newFlags) + var exemptFlags = if (requestedPermission in permissionNames) exemptMask else 0 + updatePermissionExemptFlags(appId, userId, permission, exemptMask, exemptFlags) } } } @@ -2905,5 +2826,8 @@ class PermissionService(private val service: AccessCheckingService) : } else { emptySet<String>() } + + fun getFullerPermission(permissionName: String): String? = + FULLER_PERMISSIONS[permissionName] } } diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt index cde46abafe95..96753b6d2bcc 100644 --- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt +++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt @@ -233,24 +233,6 @@ class AppIdPermissionPolicyTest : BasePermissionPolicyTest() { .isEqualTo(expectedNewFlags) } - @Test - fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() { - val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.INSTALLER_EXEMPT - testOnPackageInstalled( - oldFlags, - permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED - ) {} - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALLER_EXEMPT - assertWithMessage( - "After onPackageInstalled() is called for a non-system app that requests a runtime" + - " soft restricted permission that is exempted. The actual permission flags" + - " $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - private fun testOnPackageInstalled( oldFlags: Int, permissionInfoFlags: Int = 0, diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index 37958faed1ca..1c681ce21f02 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.res.Resources; import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; import android.view.Display; import androidx.test.core.app.ApplicationProvider; @@ -155,6 +156,7 @@ public final class DisplayBrightnessStrategySelectorTest { DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( DisplayManagerInternal.DisplayPowerRequest.class); displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; + displayPowerRequest.dozeScreenBrightness = 0.2f; when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, @@ -162,6 +164,18 @@ public final class DisplayBrightnessStrategySelectorTest { } @Test + public void selectStrategyDoesNotSelectDozeStrategyWhenInvalidBrightness() { + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; + displayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); + assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, + Display.STATE_DOZE), mDozeBrightnessModeStrategy); + } + + @Test public void selectStrategySelectsScreenOffStrategyWhenValid() { DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( DisplayManagerInternal.DisplayPowerRequest.class); diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp new file mode 100644 index 000000000000..e94b8ad0a9ac --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp @@ -0,0 +1,56 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "RollbackPackageHealthObserverTests", + + srcs: [ + "*.java", + ], + + static_libs: [ + "androidx.test.runner", + "mockito-target-extended-minus-junit4", + "services.core", + "truth", + "flag-junit", + ], + + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + certificate: "platform", + platform_apis: true, + test_suites: [ + "device-tests", + "automotive-tests", + ], +} diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml new file mode 100644 index 000000000000..c52dbdee4b4b --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.rollback"> + + <uses-sdk android:targetSdkVersion="35" /> + + <application android:testOnly="true" + android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.rollback" + android:label="Frameworks Rollback Package Health Observer test" /> +</manifest> diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml new file mode 100644 index 000000000000..635183c553bf --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> +<configuration description="Runs Rollback Package Health Observer Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="RollbackPackageHealthObserverTests.apk" /> + </target_preparer> + + <option name="test-tag" value="RollbackPackageHealthObserverTests" /> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.server.rollback" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING index e42bdad97730..6ac56bfc254a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING @@ -1,7 +1,7 @@ { "postsubmit": [ { - "name": "FrameworksMockingServicesTests", + "name": "RollbackPackageHealthObserverTests", "options": [ { "include-filter": "com.android.server.rollback" diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java new file mode 100644 index 000000000000..7ecc7fd1b94b --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wallpaper; + +import static android.app.WallpaperManager.LANDSCAPE; +import static android.app.WallpaperManager.ORIENTATION_UNKNOWN; +import static android.app.WallpaperManager.PORTRAIT; +import static android.app.WallpaperManager.SQUARE_LANDSCAPE; +import static android.app.WallpaperManager.SQUARE_PORTRAIT; +import static android.app.WallpaperManager.getOrientation; +import static android.app.WallpaperManager.getRotatedOrientation; + +import static com.android.window.flags.Flags.FLAG_MULTI_CROP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import android.graphics.Point; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.util.SparseArray; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.Comparator; +import java.util.List; + +/** + * Unit tests for the most important helpers of {@link WallpaperCropper}, in particular + * {@link WallpaperCropper#getCrop(Point, Point, SparseArray, boolean)}. + */ +@Presubmit +@RunWith(AndroidJUnit4.class) +@RequiresFlagsEnabled(FLAG_MULTI_CROP) +public class WallpaperCropperTest { + + @Mock + private WallpaperDisplayHelper mWallpaperDisplayHelper; + private WallpaperCropper mWallpaperCropper; + + private static final Point PORTRAIT_ONE = new Point(500, 800); + private static final Point PORTRAIT_TWO = new Point(400, 1000); + private static final Point PORTRAIT_THREE = new Point(2000, 800); + private static final Point PORTRAIT_FOUR = new Point(1600, 1000); + + private static final Point SQUARE_PORTRAIT_ONE = new Point(1000, 800); + private static final Point SQUARE_LANDSCAPE_ONE = new Point(800, 1000); + + /** + * Common device: a single screen of portrait/landscape orientation + */ + private static final List<Point> STANDARD_DISPLAY = List.of(PORTRAIT_ONE); + + /** 1: folded: portrait, unfolded: square with w < h */ + private static final List<Point> FOLDABLE_ONE = List.of(PORTRAIT_ONE, SQUARE_PORTRAIT_ONE); + + /** 2: folded: portrait, unfolded: square with w > h */ + private static final List<Point> FOLDABLE_TWO = List.of(PORTRAIT_TWO, SQUARE_LANDSCAPE_ONE); + + /** 3: folded: square with w < h, unfolded: portrait */ + private static final List<Point> FOLDABLE_THREE = List.of(SQUARE_PORTRAIT_ONE, PORTRAIT_THREE); + + /** 4: folded: square with w > h, unfolded: portrait */ + private static final List<Point> FOLDABLE_FOUR = List.of(SQUARE_LANDSCAPE_ONE, PORTRAIT_FOUR); + + /** + * List of different sets of displays for foldable devices. Foldable devices have two displays: + * a folded (smaller) unfolded (larger). + */ + private static final List<List<Point>> ALL_FOLDABLE_DISPLAYS = List.of( + FOLDABLE_ONE, FOLDABLE_TWO, FOLDABLE_THREE, FOLDABLE_FOUR); + + private SparseArray<Point> mDisplaySizes = new SparseArray<>(); + private int mFolded = ORIENTATION_UNKNOWN; + private int mFoldedRotated = ORIENTATION_UNKNOWN; + private int mUnfolded = ORIENTATION_UNKNOWN; + private int mUnfoldedRotated = ORIENTATION_UNKNOWN; + + private static final List<Integer> ALL_MODES = List.of( + WallpaperCropper.ADD, WallpaperCropper.REMOVE, WallpaperCropper.BALANCE); + + @Before + public void setUp() { + initMocks(this); + mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper); + } + + private void setUpWithDisplays(List<Point> displaySizes) { + mDisplaySizes = new SparseArray<>(); + displaySizes.forEach(size -> { + mDisplaySizes.put(getOrientation(size), size); + Point rotated = new Point(size.y, size.x); + mDisplaySizes.put(getOrientation(rotated), rotated); + }); + when(mWallpaperDisplayHelper.getDefaultDisplaySizes()).thenReturn(mDisplaySizes); + if (displaySizes.size() == 2) { + Point largestDisplay = displaySizes.stream().max( + Comparator.comparingInt(p -> p.x * p.y)).get(); + Point smallestDisplay = displaySizes.stream().min( + Comparator.comparingInt(p -> p.x * p.y)).get(); + mUnfolded = getOrientation(largestDisplay); + mFolded = getOrientation(smallestDisplay); + mUnfoldedRotated = getRotatedOrientation(mUnfolded); + mFoldedRotated = getRotatedOrientation(mFolded); + } + doAnswer(invocation -> getFoldedOrientation(invocation.getArgument(0))) + .when(mWallpaperDisplayHelper).getFoldedOrientation(anyInt()); + doAnswer(invocation -> getUnfoldedOrientation(invocation.getArgument(0))) + .when(mWallpaperDisplayHelper).getUnfoldedOrientation(anyInt()); + } + + private int getFoldedOrientation(int orientation) { + if (orientation == ORIENTATION_UNKNOWN) return ORIENTATION_UNKNOWN; + if (orientation == mUnfolded) return mFolded; + if (orientation == mUnfoldedRotated) return mFoldedRotated; + return ORIENTATION_UNKNOWN; + } + + private int getUnfoldedOrientation(int orientation) { + if (orientation == ORIENTATION_UNKNOWN) return ORIENTATION_UNKNOWN; + if (orientation == mFolded) return mUnfolded; + if (orientation == mFoldedRotated) return mUnfoldedRotated; + return ORIENTATION_UNKNOWN; + } + + /** + * Test that {@link WallpaperCropper#noParallax} successfully removes the parallax in a simple + * case, removing the right or left part depending on the "rtl" argument. + */ + @Test + public void testNoParallax_noScale() { + Point displaySize = new Point(1000, 1000); + Point bitmapSize = new Point(1200, 1000); + Point expectedCropSize = new Point(1000, 1000); + Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ false)) + .isEqualTo(leftOf(crop, expectedCropSize)); + assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ true)) + .isEqualTo(rightOf(crop, expectedCropSize)); + } + + /** + * Test that {@link WallpaperCropper#noParallax} correctly takes zooming into account. + */ + @Test + public void testNoParallax_withScale() { + Point displaySize = new Point(1000, 1000); + Point bitmapSize = new Point(600, 500); + Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + Point expectedCropSize = new Point(500, 500); + assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ false)) + .isEqualTo(leftOf(crop, expectedCropSize)); + assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ true)) + .isEqualTo(rightOf(crop, expectedCropSize)); + } + + /** + * Test that {@link WallpaperCropper#noParallax} correctly removes parallax when the image is + * cropped, i.e. when the crop rectangle is not the full bitmap. + */ + @Test + public void testNoParallax_withScaleAndCrop() { + Point displaySize = new Point(1000, 1000); + Point bitmapSize = new Point(2000, 2000); + Rect crop = new Rect(300, 1000, 900, 1500); + Point expectedCropSize = new Point(500, 500); + assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ false)) + .isEqualTo(leftOf(crop, expectedCropSize)); + assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ true)) + .isEqualTo(rightOf(crop, expectedCropSize)); + } + + /** + * Test that {@link WallpaperCropper#getAdjustedCrop} does nothing when the crop has the same + * width/height ratio than the screen. + */ + @Test + public void testGetAdjustedCrop_noOp() { + Point displaySize = new Point(1000, 1000); + + for (Point bitmapSize: List.of( + new Point(1000, 1000), + new Point(2000, 2000), + new Point(500, 500))) { + for (Rect crop: List.of( + new Rect(0, 0, bitmapSize.x, bitmapSize.y), + new Rect(100, 200, bitmapSize.x - 100, bitmapSize.y))) { + for (int mode: ALL_MODES) { + for (boolean rtl: List.of(true, false)) { + for (boolean parallax: List.of(true, false)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, parallax, rtl, mode)) + .isEqualTo(crop); + } + } + } + } + } + } + + /** + * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with parallax = true, + * does not keep more width than needed for {@link WallpaperCropper#MAX_PARALLAX}. + */ + @Test + public void testGetAdjustedCrop_tooMuchParallax() { + Point displaySize = new Point(1000, 1000); + int tooLargeWidth = (int) (displaySize.x * (1 + 2 * WallpaperCropper.MAX_PARALLAX)); + Point bitmapSize = new Point(tooLargeWidth, 1000); + Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + int expectedWidth = (int) (displaySize.x * (1 + WallpaperCropper.MAX_PARALLAX)); + Point expectedCropSize = new Point(expectedWidth, 1000); + for (int mode: ALL_MODES) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, true, false, mode)) + .isEqualTo(leftOf(crop, expectedCropSize)); + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, true, true, mode)) + .isEqualTo(rightOf(crop, expectedCropSize)); + } + } + + /** + * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with parallax = true, + * does not remove parallax if the parallax is below {@link WallpaperCropper#MAX_PARALLAX}. + */ + @Test + public void testGetAdjustedCrop_acceptableParallax() { + Point displaySize = new Point(1000, 1000); + List<Integer> acceptableWidths = List.of(displaySize.x, + (int) (displaySize.x * (1 + 0.5 * WallpaperCropper.MAX_PARALLAX)), + (int) (displaySize.x * (1 + 0.9 * WallpaperCropper.MAX_PARALLAX)), + (int) (displaySize.x * (1 + 1.0 * WallpaperCropper.MAX_PARALLAX))); + for (int acceptableWidth: acceptableWidths) { + Point bitmapSize = new Point(acceptableWidth, 1000); + Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + for (int mode : ALL_MODES) { + for (boolean rtl : List.of(false, true)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, true, rtl, mode)) + .isEqualTo(crop); + } + } + } + } + + /** + * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with + * {@link WallpaperCropper#ADD}, correctly enlarges the crop to match the display dimensions, + * and adds content to the crop by an equal amount on both sides when possible. + */ + @Test + public void testGetAdjustedCrop_add() { + Point displaySize = new Point(1000, 1000); + Point bitmapSize = new Point(1000, 1000); + + List<Rect> crops = List.of( + new Rect(0, 0, 900, 1000), + new Rect(0, 0, 1000, 900), + new Rect(0, 0, 400, 500), + new Rect(500, 600, 1000, 1000)); + + List<Rect> expectedAdjustedCrops = List.of( + new Rect(0, 0, 1000, 1000), + new Rect(0, 0, 1000, 1000), + new Rect(0, 0, 500, 500), + new Rect(500, 500, 1000, 1000)); + + for (int i = 0; i < crops.size(); i++) { + Rect crop = crops.get(i); + Rect expectedCrop = expectedAdjustedCrops.get(i); + for (boolean rtl: List.of(false, true)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.ADD)) + .isEqualTo(expectedCrop); + } + } + } + + /** + * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with + * {@link WallpaperCropper#REMOVE}, correctly shrinks the crop to match the display dimensions, + * and removes content by an equal amount on both sides. + */ + @Test + public void testGetAdjustedCrop_remove() { + Point displaySize = new Point(1000, 1000); + Point bitmapSize = new Point(1500, 1500); + + List<Rect> crops = List.of( + new Rect(50, 0, 1150, 1000), + new Rect(0, 50, 1000, 1150)); + + Point expectedCropSize = new Point(1000, 1000); + + for (Rect crop: crops) { + for (boolean rtl : List.of(false, true)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.REMOVE)) + .isEqualTo(centerOf(crop, expectedCropSize)); + } + } + } + + /** + * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with + * {@link WallpaperCropper#BALANCE}, gives an adjusted crop with the same center and same number + * of pixels when possible. + */ + @Test + public void testGetAdjustedCrop_balance() { + Point displaySize = new Point(500, 1000); + Point transposedDisplaySize = new Point(1000, 500); + Point bitmapSize = new Point(1000, 1000); + + List<Rect> crops = List.of( + new Rect(0, 250, 1000, 750), + new Rect(100, 0, 300, 100)); + + List<Rect> expectedAdjustedCrops = List.of( + new Rect(250, 0, 750, 1000), + new Rect(150, 0, 250, 200)); + + for (int i = 0; i < crops.size(); i++) { + Rect crop = crops.get(i); + Rect expected = expectedAdjustedCrops.get(i); + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, false, false, WallpaperCropper.BALANCE)) + .isEqualTo(expected); + + Rect transposedCrop = new Rect(crop.top, crop.left, crop.bottom, crop.right); + Rect expectedTransposed = new Rect( + expected.top, expected.left, expected.bottom, expected.right); + assertThat(WallpaperCropper.getAdjustedCrop(transposedCrop, bitmapSize, + transposedDisplaySize, false, false, WallpaperCropper.BALANCE)) + .isEqualTo(expectedTransposed); + } + } + + /** + * Test that {@link WallpaperCropper#getCrop} follows a simple centre-align strategy when + * no suggested crops are provided. + */ + @Test + public void testGetCrop_noSuggestedCrops_centersWallpaper() { + setUpWithDisplays(STANDARD_DISPLAY); + Point bitmapSize = new Point(800, 1000); + Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + + List<Point> displaySizes = List.of( + new Point(500, 1000), + new Point(1000, 500)); + List<Point> expectedCropSizes = List.of( + new Point(500, 1000), + new Point(800, 400)); + + for (int i = 0; i < displaySizes.size(); i++) { + Point displaySize = displaySizes.get(i); + Point expectedCropSize = expectedCropSizes.get(i); + for (boolean rtl : List.of(false, true)) { + assertThat(mWallpaperCropper.getCrop( + displaySize, bitmapSize, suggestedCrops, rtl)) + .isEqualTo(centerOf(bitmapRect, expectedCropSize)); + } + } + } + + /** + * Test that {@link WallpaperCropper#getCrop} reuses a suggested crop of the same orientation + * as the display if possible, and does not remove additional width for parallax, + * but adds width if necessary. + */ + @Test + public void testGetCrop_hasSuggestedCrop() { + setUpWithDisplays(STANDARD_DISPLAY); + Point bitmapSize = new Point(800, 1000); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + suggestedCrops.put(PORTRAIT, new Rect(0, 0, 400, 800)); + for (int otherOrientation: List.of(LANDSCAPE, SQUARE_LANDSCAPE, SQUARE_PORTRAIT)) { + suggestedCrops.put(otherOrientation, new Rect(0, 0, 10, 10)); + } + + for (boolean rtl : List.of(false, true)) { + assertThat(mWallpaperCropper.getCrop( + new Point(300, 800), bitmapSize, suggestedCrops, rtl)) + .isEqualTo(suggestedCrops.get(PORTRAIT)); + assertThat(mWallpaperCropper.getCrop( + new Point(500, 800), bitmapSize, suggestedCrops, rtl)) + .isEqualTo(new Rect(0, 0, 500, 800)); + } + } + + /** + * Test that {@link WallpaperCropper#getCrop}, if there is no suggested crop of the same + * orientation as the display, reuses a suggested crop of the rotated orientation if possible, + * and preserves the center and number of pixels of the crop if possible. + * <p> + * To simplify, in this test case all crops have the same size as the display (no zoom) + * and are at the center of the image. Also the image is large enough to preserver the number + * of pixels (no additional zoom required). + */ + @Test + public void testGetCrop_hasRotatedSuggestedCrop() { + setUpWithDisplays(STANDARD_DISPLAY); + Point bitmapSize = new Point(2000, 1800); + Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + Point portrait = PORTRAIT_ONE; + Point landscape = new Point(PORTRAIT_ONE.y, PORTRAIT_ONE.x); + Point squarePortrait = SQUARE_PORTRAIT_ONE; + Point squareLandscape = new Point(SQUARE_PORTRAIT_ONE.y, SQUARE_PORTRAIT_ONE.y); + suggestedCrops.put(PORTRAIT, centerOf(bitmapRect, portrait)); + suggestedCrops.put(SQUARE_LANDSCAPE, centerOf(bitmapRect, squareLandscape)); + for (boolean rtl : List.of(false, true)) { + assertThat(mWallpaperCropper.getCrop( + landscape, bitmapSize, suggestedCrops, rtl)) + .isEqualTo(centerOf(bitmapRect, landscape)); + + assertThat(mWallpaperCropper.getCrop( + squarePortrait, bitmapSize, suggestedCrops, rtl)) + .isEqualTo(centerOf(bitmapRect, squarePortrait)); + } + } + + /** + * Test that {@link WallpaperCropper#getCrop}, when asked for a folded crop with a suggested + * crop only for the relative unfolded orientation, creates the folded crop at the center of the + * unfolded crop, by removing content on two sides to match the folded screen dimensions. + * <p> + * To simplify, in this test case all crops have the same size as the display (no zoom) + * and are at the center of the image. + */ + @Test + public void testGetCrop_hasUnfoldedSuggestedCrop() { + for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) { + setUpWithDisplays(displaySizes); + Point bitmapSize = new Point(2000, 2400); + Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + + Point largestDisplay = displaySizes.stream().max( + Comparator.comparingInt(a -> a.x * a.y)).orElseThrow(); + int unfoldedOne = getOrientation(largestDisplay); + int unfoldedTwo = getRotatedOrientation(unfoldedOne); + Rect unfoldedCropOne = centerOf(bitmapRect, mDisplaySizes.get(unfoldedOne)); + Rect unfoldedCropTwo = centerOf(bitmapRect, mDisplaySizes.get(unfoldedTwo)); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + suggestedCrops.put(unfoldedOne, unfoldedCropOne); + suggestedCrops.put(unfoldedTwo, unfoldedCropTwo); + + int foldedOne = getFoldedOrientation(unfoldedOne); + int foldedTwo = getFoldedOrientation(unfoldedTwo); + Point foldedDisplayOne = mDisplaySizes.get(foldedOne); + Point foldedDisplayTwo = mDisplaySizes.get(foldedTwo); + + for (boolean rtl : List.of(false, true)) { + assertThat(mWallpaperCropper.getCrop( + foldedDisplayOne, bitmapSize, suggestedCrops, rtl)) + .isEqualTo(centerOf(unfoldedCropOne, foldedDisplayOne)); + + assertThat(mWallpaperCropper.getCrop( + foldedDisplayTwo, bitmapSize, suggestedCrops, rtl)) + .isEqualTo(centerOf(unfoldedCropTwo, foldedDisplayTwo)); + } + } + } + + /** + * Test that {@link WallpaperCropper#getCrop}, when asked for an unfolded crop with a suggested + * crop only for the relative folded orientation, creates the unfolded crop with the same center + * as the folded crop, by adding content on two sides to match the unfolded screen dimensions. + * <p> + * To simplify, in this test case all crops have the same size as the display (no zoom) and are + * at the center of the image. Also the image is large enough to add content. + */ + @Test + public void testGetCrop_hasFoldedSuggestedCrop() { + for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) { + setUpWithDisplays(displaySizes); + Point bitmapSize = new Point(2000, 2000); + Rect bitmapRect = new Rect(0, 0, 2000, 2000); + + Point smallestDisplay = displaySizes.stream().min( + Comparator.comparingInt(a -> a.x * a.y)).orElseThrow(); + int foldedOne = getOrientation(smallestDisplay); + int foldedTwo = getRotatedOrientation(foldedOne); + Point foldedDisplayOne = mDisplaySizes.get(foldedOne); + Point foldedDisplayTwo = mDisplaySizes.get(foldedTwo); + Rect foldedCropOne = centerOf(bitmapRect, foldedDisplayOne); + Rect foldedCropTwo = centerOf(bitmapRect, foldedDisplayTwo); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + suggestedCrops.put(foldedOne, foldedCropOne); + suggestedCrops.put(foldedTwo, foldedCropTwo); + + int unfoldedOne = getUnfoldedOrientation(foldedOne); + int unfoldedTwo = getUnfoldedOrientation(foldedTwo); + Point unfoldedDisplayOne = mDisplaySizes.get(unfoldedOne); + Point unfoldedDisplayTwo = mDisplaySizes.get(unfoldedTwo); + + for (boolean rtl : List.of(false, true)) { + assertThat(centerOf(mWallpaperCropper.getCrop( + unfoldedDisplayOne, bitmapSize, suggestedCrops, rtl), foldedDisplayOne)) + .isEqualTo(foldedCropOne); + + assertThat(centerOf(mWallpaperCropper.getCrop( + unfoldedDisplayTwo, bitmapSize, suggestedCrops, rtl), foldedDisplayTwo)) + .isEqualTo(foldedCropTwo); + } + } + } + + /** + * Test that {@link WallpaperCropper#getCrop}, when asked for an folded crop with a suggested + * crop only for the rotated unfolded orientation, creates the folded crop from that crop by + * combining a rotate + fold operation. The folded crop should have less pixels than the + * unfolded crop due to the fold operation which removes content on both sides of the image. + * <p> + * To simplify, in this test case all crops have the same size as the display (no zoom) + * and are at the center of the image. + */ + @Test + public void testGetCrop_hasRotatedUnfoldedSuggestedCrop() { + for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) { + setUpWithDisplays(displaySizes); + Point bitmapSize = new Point(2000, 2000); + Rect bitmapRect = new Rect(0, 0, 2000, 2000); + Point largestDisplay = displaySizes.stream().max( + Comparator.comparingInt(a -> a.x * a.y)).orElseThrow(); + int unfoldedOne = getOrientation(largestDisplay); + int unfoldedTwo = getRotatedOrientation(unfoldedOne); + for (int unfolded: List.of(unfoldedOne, unfoldedTwo)) { + Rect unfoldedCrop = centerOf(bitmapRect, mDisplaySizes.get(unfolded)); + int rotatedUnfolded = getRotatedOrientation(unfolded); + Rect rotatedUnfoldedCrop = centerOf(bitmapRect, mDisplaySizes.get(rotatedUnfolded)); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + suggestedCrops.put(unfolded, unfoldedCrop); + int rotatedFolded = getFoldedOrientation(rotatedUnfolded); + Point rotatedFoldedDisplay = mDisplaySizes.get(rotatedFolded); + + for (boolean rtl : List.of(false, true)) { + assertThat(mWallpaperCropper.getCrop( + rotatedFoldedDisplay, bitmapSize, suggestedCrops, rtl)) + .isEqualTo(centerOf(rotatedUnfoldedCrop, rotatedFoldedDisplay)); + } + } + } + } + + /** + * Test that {@link WallpaperCropper#getCrop}, when asked for an unfolded crop with a suggested + * crop only for the rotated folded orientation, creates the unfolded crop from that crop by + * combining a rotate + unfold operation. The unfolded crop should have more pixels than the + * folded crop due to the unfold operation which adds content on two sides of the image. + * <p> + * To simplify, in this test case all crops have the same size as the display (no zoom) + * and are centered inside the image. Also the image is large enough to add content. + */ + @Test + public void testGetCrop_hasRotatedFoldedSuggestedCrop() { + for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) { + setUpWithDisplays(displaySizes); + Point bitmapSize = new Point(2000, 2000); + Rect bitmapRect = new Rect(0, 0, 2000, 2000); + + Point smallestDisplay = displaySizes.stream().min( + Comparator.comparingInt(a -> a.x * a.y)).orElseThrow(); + int foldedOne = getOrientation(smallestDisplay); + int foldedTwo = getRotatedOrientation(foldedOne); + for (int folded: List.of(foldedOne, foldedTwo)) { + Rect foldedCrop = centerOf(bitmapRect, mDisplaySizes.get(folded)); + SparseArray<Rect> suggestedCrops = new SparseArray<>(); + suggestedCrops.put(folded, foldedCrop); + int rotatedFolded = getRotatedOrientation(folded); + int rotatedUnfolded = getUnfoldedOrientation(rotatedFolded); + Point rotatedFoldedDisplay = mDisplaySizes.get(rotatedFolded); + Rect rotatedFoldedCrop = centerOf(bitmapRect, rotatedFoldedDisplay); + Point rotatedUnfoldedDisplay = mDisplaySizes.get(rotatedUnfolded); + + for (boolean rtl : List.of(false, true)) { + Rect rotatedUnfoldedCrop = mWallpaperCropper.getCrop( + rotatedUnfoldedDisplay, bitmapSize, suggestedCrops, rtl); + assertThat(centerOf(rotatedUnfoldedCrop, rotatedFoldedDisplay)) + .isEqualTo(rotatedFoldedCrop); + } + } + } + } + + private static Rect centerOf(Rect container, Point point) { + checkSubset(container, point); + int diffWidth = container.width() - point.x; + int diffHeight = container.height() - point.y; + int startX = container.left + diffWidth / 2; + int startY = container.top + diffHeight / 2; + return new Rect(startX, startY, startX + point.x, startY + point.y); + } + + private static Rect leftOf(Rect container, Point point) { + Rect result = centerOf(container, point); + result.offset(container.left - result.left, 0); + return result; + } + + private static Rect rightOf(Rect container, Point point) { + checkSubset(container, point); + Rect result = centerOf(container, point); + result.offset(container.right - result.right, 0); + return result; + } + + private static void checkSubset(Rect container, Point point) { + if (container.width() < point.x || container.height() < point.y) { + throw new IllegalArgumentException(); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java index bb0063427339..fa1fd90e10c9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java @@ -344,6 +344,21 @@ public class ALSProbeTest { verifyNoMoreInteractions(mSensorManager); } + @Test + public void testAwaitLuxWhenNoLightSensor() { + when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(null); + mProbe = new ALSProbe(mSensorManager, new Handler(mLooper.getLooper()), TIMEOUT_MS - 1); + + AtomicInteger lux = new AtomicInteger(-5); + mProbe.awaitNextLux((v) -> lux.set(Math.round(v)), null /* handler */); + + // Verify that no light sensor will be registered. + verify(mSensorManager, times(0)).registerListener( + mSensorEventListenerCaptor.capture(), any(), anyInt()); + + assertThat(lux.get()).isEqualTo(-1); + } + private void moveTimeBy(long millis) { mLooper.moveTimeForward(millis); mLooper.processAllMessages(); diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java index ea84eb2fbf73..e0ef035de735 100644 --- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java @@ -16,8 +16,6 @@ package com.android.server.os; -import android.app.admin.flags.Flags; - import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static com.google.common.truth.Truth.assertThat; @@ -27,15 +25,19 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import android.app.admin.DevicePolicyManager; +import android.app.admin.flags.Flags; import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.BugreportManager.BugreportCallback; +import android.os.BugreportParams; import android.os.IBinder; import android.os.IDumpstateListener; import android.os.Process; import android.os.RemoteException; +import android.os.UserManager; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; @@ -75,6 +77,10 @@ public class BugreportManagerServiceImplTest { @Mock private PackageManager mPackageManager; + @Mock + private UserManager mMockUserManager; + @Mock + private DevicePolicyManager mMockDevicePolicyManager; private int mCallingUid = 1234; private String mCallingPackage = "test.package"; @@ -91,10 +97,12 @@ public class BugreportManagerServiceImplTest { ArraySet<String> mAllowlistedPackages = new ArraySet<>(); mAllowlistedPackages.add(mContext.getPackageName()); mService = new BugreportManagerServiceImpl( - new BugreportManagerServiceImpl.Injector(mContext, mAllowlistedPackages, - mMappingFile)); + new TestInjector(mContext, mAllowlistedPackages, mMappingFile, + mMockUserManager, mMockDevicePolicyManager)); mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager(mMappingFile); when(mPackageManager.getPackageUidAsUser(anyString(), anyInt())).thenReturn(mCallingUid); + // The calling user is an admin user by default. + when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(true); } @After @@ -182,6 +190,36 @@ public class BugreportManagerServiceImplTest { } @Test + public void testStartBugreport_throwsForNonAdminUser() throws Exception { + when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false); + + Exception thrown = assertThrows(IllegalArgumentException.class, + () -> mService.startBugreport(mCallingUid, mContext.getPackageName(), + new FileDescriptor(), /* screenshotFd= */ null, + BugreportParams.BUGREPORT_MODE_FULL, + /* flags= */ 0, new Listener(new CountDownLatch(1)), + /* isScreenshotRequested= */ false)); + + assertThat(thrown.getMessage()).contains("not an admin user"); + } + + @Test + public void testStartBugreport_throwsForNotAffiliatedUser() throws Exception { + when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false); + when(mMockDevicePolicyManager.getDeviceOwnerUserId()).thenReturn(-1); + when(mMockDevicePolicyManager.isAffiliatedUser(anyInt())).thenReturn(false); + + Exception thrown = assertThrows(IllegalArgumentException.class, + () -> mService.startBugreport(mCallingUid, mContext.getPackageName(), + new FileDescriptor(), /* screenshotFd= */ null, + BugreportParams.BUGREPORT_MODE_REMOTE, + /* flags= */ 0, new Listener(new CountDownLatch(1)), + /* isScreenshotRequested= */ false)); + + assertThat(thrown.getMessage()).contains("not affiliated to the device owner"); + } + + @Test public void testRetrieveBugreportWithoutFilesForCaller() throws Exception { CountDownLatch latch = new CountDownLatch(1); Listener listener = new Listener(latch); @@ -224,7 +262,8 @@ public class BugreportManagerServiceImplTest { private void clearAllowlist() { mService = new BugreportManagerServiceImpl( - new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>(), mMappingFile)); + new TestInjector(mContext, new ArraySet<>(), mMappingFile, + mMockUserManager, mMockDevicePolicyManager)); } private static class Listener implements IDumpstateListener { @@ -275,4 +314,27 @@ public class BugreportManagerServiceImplTest { complete(successful); } } + + private static class TestInjector extends BugreportManagerServiceImpl.Injector { + + private final UserManager mUserManager; + private final DevicePolicyManager mDevicePolicyManager; + + TestInjector(Context context, ArraySet<String> allowlistedPackages, AtomicFile mappingFile, + UserManager um, DevicePolicyManager dpm) { + super(context, allowlistedPackages, mappingFile); + mUserManager = um; + mDevicePolicyManager = dpm; + } + + @Override + public UserManager getUserManager() { + return mUserManager; + } + + @Override + public DevicePolicyManager getDevicePolicyManager() { + return mDevicePolicyManager; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java index 2039f93b9c40..54d11387752c 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java +++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java @@ -96,6 +96,9 @@ public class TestSystemImpl implements SystemInterface { return; } PackageInfo packageInfo = userPackages.get(userId); + if (packageInfo == null) { + return; + } packageInfo.applicationInfo.enabled = enable; setPackageInfoForUser(userId, packageInfo); } @@ -106,6 +109,9 @@ public class TestSystemImpl implements SystemInterface { return; } PackageInfo packageInfo = userPackages.get(userId); + if (packageInfo == null) { + return; + } packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; packageInfo.applicationInfo.privateFlags &= (~ApplicationInfo.PRIVATE_FLAG_HIDDEN); setPackageInfoForUser(userId, packageInfo); diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java index c535ec5026db..e181a513b637 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java @@ -1551,7 +1551,7 @@ public class WebViewUpdateServiceTest { @Test @RequiresFlagsEnabled("android.webkit.update_service_v2") - public void testDefaultWebViewPackageInstalling() { + public void testDefaultWebViewPackageInstallingDuringStartUp() { String testPackage = "testDefault"; WebViewProviderInfo[] packages = new WebViewProviderInfo[] { @@ -1574,6 +1574,68 @@ public class WebViewUpdateServiceTest { Matchers.anyObject(), Mockito.eq(testPackage)); } + @Test + @RequiresFlagsEnabled("android.webkit.update_service_v2") + public void testDefaultWebViewPackageInstallingAfterStartUp() { + String testPackage = "testDefault"; + WebViewProviderInfo[] packages = + new WebViewProviderInfo[] { + new WebViewProviderInfo( + testPackage, + "", + true /* default available */, + false /* fallback */, + null) + }; + checkCertainPackageUsedAfterWebViewBootPreparation(testPackage, packages); + + // uninstall the default package. + mTestSystemImpl.setPackageInfo( + createPackageInfo( + testPackage, true /* enabled */, true /* valid */, false /* installed */)); + mWebViewUpdateServiceImpl.packageStateChanged(testPackage, + WebViewUpdateService.PACKAGE_REMOVED, 0); + + // Check that we try to re-install the default package. + Mockito.verify(mTestSystemImpl) + .installExistingPackageForAllUsers( + Matchers.anyObject(), Mockito.eq(testPackage)); + } + + /** + * Ensures that adding a new user for which the current WebView package is uninstalled triggers + * the repair logic. + */ + @Test + @RequiresFlagsEnabled("android.webkit.update_service_v2") + public void testAddingNewUserWithDefaultdPackageNotInstalled() { + String testPackage = "testDefault"; + WebViewProviderInfo[] packages = + new WebViewProviderInfo[] { + new WebViewProviderInfo( + testPackage, + "", + true /* default available */, + false /* fallback */, + null) + }; + checkCertainPackageUsedAfterWebViewBootPreparation(testPackage, packages); + + // Add new user with the default package not installed. + int newUser = 100; + mTestSystemImpl.addUser(newUser); + mTestSystemImpl.setPackageInfoForUser(newUser, + createPackageInfo(testPackage, true /* enabled */, true /* valid */, + false /* installed */)); + + mWebViewUpdateServiceImpl.handleNewUser(newUser); + + // Check that we try to re-install the default package for all users. + Mockito.verify(mTestSystemImpl) + .installExistingPackageForAllUsers( + Matchers.anyObject(), Mockito.eq(testPackage)); + } + private void testDefaultPackageChosen(PackageInfo packageInfo) { WebViewProviderInfo[] packages = new WebViewProviderInfo[] { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 33ca5c2bbe16..a45b102278ef 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; @@ -32,6 +33,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.app.INotificationManager; import android.content.ComponentName; import android.content.Context; @@ -47,9 +49,12 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.Xml; +import android.Manifest; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.function.TriPredicate; import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; import com.android.server.UiServiceTestCase; import com.android.server.notification.NotificationManagerService.NotificationAssistants; @@ -59,7 +64,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -89,11 +96,15 @@ public class NotificationAssistantsTest extends UiServiceTestCase { UserInfo mZero = new UserInfo(0, "zero", 0); UserInfo mTen = new UserInfo(10, "ten", 0); + ComponentName mCn = new ComponentName("a", "b"); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext.setMockPackageManager(mPm); mContext.addMockSystemService(Context.USER_SERVICE, mUm); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.string.config_defaultAssistantAccessComponent, "a/a"); mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm)); when(mNm.getBinderService()).thenReturn(mINm); mContext.ensureTestableResources(); @@ -102,8 +113,9 @@ public class NotificationAssistantsTest extends UiServiceTestCase { ResolveInfo resolve = new ResolveInfo(); approved.add(resolve); ServiceInfo info = new ServiceInfo(); - info.packageName = "a"; - info.name="a"; + info.packageName = mCn.getPackageName(); + info.name = mCn.getClassName(); + info.permission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE; resolve.serviceInfo = info; when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())) .thenReturn(approved); @@ -137,6 +149,51 @@ public class NotificationAssistantsTest extends UiServiceTestCase { } @Test + public void testWriteXml_userTurnedOffNAS() throws Exception { + int userId = ActivityManager.getCurrentUser(); + + mAssistants.loadDefaultsFromConfig(true); + + mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true, + true, true); + + ComponentName current = CollectionUtils.firstOrNull( + mAssistants.getAllowedComponents(userId)); + assertNotNull(current); + mAssistants.setUserSet(userId, true); + mAssistants.setPackageOrComponentEnabled(current.flattenToString(), userId, true, false, + true); + + TypedXmlSerializer serializer = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mAssistants.writeXml(serializer, true, userId); + serializer.endDocument(); + serializer.flush(); + + //fail(baos.toString("UTF-8")); + + final TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + TriPredicate<String, Integer, String> allowedManagedServicePackages = + mNm::canUseManagedServices; + + parser.nextTag(); + mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm)); + mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL); + + ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0); + // approved should not be null + assertNotNull(approved); + assertEquals(new ArraySet<>(), approved.get(true)); + + // user set is maintained + assertTrue(mAssistants.mIsUserChanged.get(ActivityManager.getCurrentUser())); + } + + @Test public void testReadXml_userDisabled() throws Exception { String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">" + "<service_listing approved=\"\" user=\"0\" primary=\"true\"" @@ -160,6 +217,33 @@ public class NotificationAssistantsTest extends UiServiceTestCase { } @Test + public void testReadXml_userDisabled_restore() throws Exception { + String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">" + + "<service_listing approved=\"\" user=\"0\" primary=\"true\"" + + "user_changed=\"true\"/>" + + "</enabled_assistants>"; + + final TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml.toString().getBytes())), null); + TriPredicate<String, Integer, String> allowedManagedServicePackages = + mNm::canUseManagedServices; + + parser.nextTag(); + mAssistants.readXml(parser, allowedManagedServicePackages, true, + ActivityManager.getCurrentUser()); + + ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0); + + // approved should not be null + assertNotNull(approved); + assertEquals(new ArraySet<>(), approved.get(true)); + + // user set is maintained + assertTrue(mAssistants.mIsUserChanged.get(ActivityManager.getCurrentUser())); + } + + @Test public void testReadXml_upgradeUserSet() throws Exception { String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">" + "<service_listing approved=\"\" user=\"0\" primary=\"true\"" diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java index 3499a12f5954..bf8cfa5c0561 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java @@ -20,8 +20,12 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertFalse; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -70,10 +74,11 @@ public class NotificationHistoryJobServiceTest extends UiServiceTestCase { @Before public void setUp() throws Exception { - mJobService = new NotificationHistoryJobService(); + mJobService = spy(new NotificationHistoryJobService()); mJobService.attachBaseContext(mContext); mJobService.onCreate(); mJobService.onBind(/* intent= */ null); // Create JobServiceEngine within JobService. + doNothing().when(mJobService).jobFinished(any(), eq(false)); mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java index 99d5a6d9118a..75552bc433c5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java @@ -16,7 +16,7 @@ package com.android.server.notification; -import static com.android.server.notification.ZenAdapters.notificationPolicyToZenPolicy; +import static android.service.notification.ZenAdapters.notificationPolicyToZenPolicy; import static com.google.common.truth.Truth.assertThat; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 495e01a640de..7c1adbc39033 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -136,6 +136,7 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.Condition; import android.service.notification.DeviceEffectsApplier; +import android.service.notification.ZenAdapters; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ConfigChangeOrigin; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 60130635108c..da11e6ad613a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -554,7 +554,7 @@ public class RootWindowContainerTests extends WindowTestsBase { mRootWindowContainer.applySleepTokens(true); // The display orientation should be changed by the activity so there is no relaunch. - verify(activity, never()).relaunchActivityLocked(anyBoolean()); + verify(activity, never()).relaunchActivityLocked(anyBoolean(), anyInt()); assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation); } diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 44f4068fb424..883c702ddb79 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -383,7 +383,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage); } else { // Multiple packages means we need to go manual - final int appId = UserHandle.getUserId(appInfo.uid); + final int appId = UserHandle.getAppId(appInfo.uid); final String[] packageNames = new String[] { packageName }; final long[] ceDataInodes = new long[1]; String[] codePaths = new String[0]; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 1e1dd00b8df5..5a5296836089 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -66,7 +66,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; -import android.os.PermissionEnforcer; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteCallbackList; @@ -76,7 +75,6 @@ import android.os.SharedMemory; import android.os.ShellCallback; import android.os.Trace; import android.os.UserHandle; -import android.permission.flags.Flags; import android.provider.Settings; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback; @@ -1407,17 +1405,6 @@ public class VoiceInteractionManagerService extends SystemService { } } - // Enforce permissions that are flag controlled. The flag value decides if the permission - // should be enforced. - private void initAndVerifyDetector_enforcePermissionWithFlags() { - PermissionEnforcer enforcer = mContext.getSystemService(PermissionEnforcer.class); - if (Flags.voiceActivationPermissionApis()) { - enforcer.enforcePermission( - android.Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO, - getCallingPid(), getCallingUid()); - } - } - @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) @Override public void initAndVerifyDetector( @@ -1427,13 +1414,7 @@ public class VoiceInteractionManagerService extends SystemService { @NonNull IBinder token, IHotwordRecognitionStatusCallback callback, int detectorType) { - // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the - // {@link #initAndVerifyDetector(Identity, PersistableBundle, ShareMemory, IBinder, - // IHotwordRecognitionStatusCallback, int)} - // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully - // launched. super.initAndVerifyDetector_enforcePermission(); - initAndVerifyDetector_enforcePermissionWithFlags(); synchronized (this) { enforceIsCurrentVoiceInteractionService(); diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 9792cdd80a00..048b1b290dde 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1434,11 +1434,14 @@ public class TelecomManager { } /** - * This API will return all {@link PhoneAccount}s registered via - * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears - * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount} - * or the caller registered the {@link PhoneAccount} under a different user and does not - * have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission. + * This API will return all {@link PhoneAccount}s the caller registered via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears + * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount} (for + * cleanup purposes) or the caller registered the {@link PhoneAccount} under a different user + * and does not have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission. + * <b>Note:</b> This API will only return {@link PhoneAccount}s registered by the same app. For + * system Dialers that need all the {@link PhoneAccount}s registered by every application, see + * {@link TelecomManager#getAllPhoneAccounts()}. * * @return all the {@link PhoneAccount}s registered by the caller. */ diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java index c2f5b8f3e7da..901daf813e86 100644 --- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java +++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java @@ -19,12 +19,14 @@ package android.telephony; import android.annotation.NonNull; import android.app.SystemServiceRegistry; import android.content.Context; +import android.content.pm.PackageManager; import android.os.TelephonyServiceManager; import android.telephony.euicc.EuiccCardManager; import android.telephony.euicc.EuiccManager; import android.telephony.ims.ImsManager; import android.telephony.satellite.SatelliteManager; +import com.android.internal.telephony.flags.Flags; import com.android.internal.util.Preconditions; @@ -55,6 +57,11 @@ public class TelephonyFrameworkInitializer { sTelephonyServiceManager = Preconditions.checkNotNull(telephonyServiceManager); } + private static boolean hasSystemFeature(Context context, String feature) { + if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true; + return context.getPackageManager().hasSystemFeature(feature); + } + /** * Called by {@link SystemServiceRegistry}'s static initializer and registers all telephony * services to {@link Context}, so that {@link Context#getSystemService} can return them. @@ -76,33 +83,39 @@ public class TelephonyFrameworkInitializer { SystemServiceRegistry.registerContextAwareService( Context.CARRIER_CONFIG_SERVICE, CarrierConfigManager.class, - context -> new CarrierConfigManager(context) + context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + ? new CarrierConfigManager(context) : null ); SystemServiceRegistry.registerContextAwareService( Context.EUICC_SERVICE, EuiccManager.class, - context -> new EuiccManager(context) + context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_EUICC) + ? new EuiccManager(context) : null ); SystemServiceRegistry.registerContextAwareService( Context.EUICC_CARD_SERVICE, EuiccCardManager.class, - context -> new EuiccCardManager(context) + context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_EUICC) + ? new EuiccCardManager(context) : null ); SystemServiceRegistry.registerContextAwareService( Context.TELEPHONY_IMS_SERVICE, ImsManager.class, - context -> new ImsManager(context) + context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_IMS) + ? new ImsManager(context) : null ); SystemServiceRegistry.registerContextAwareService( Context.SMS_SERVICE, SmsManager.class, - context -> SmsManager.getSmsManagerForContextAndSubscriptionId(context, - SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) + context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_MESSAGING) + ? SmsManager.getSmsManagerForContextAndSubscriptionId(context, + SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) : null ); SystemServiceRegistry.registerContextAwareService( Context.SATELLITE_SERVICE, SatelliteManager.class, - context -> new SatelliteManager(context) + context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_SATELLITE) + ? new SatelliteManager(context) : null ); } diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt index d47329ea97b4..57da05f13bbb 100644 --- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt @@ -52,6 +52,7 @@ class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) : // Can't use TAPL due to Recents not showing in 3 Button Nav in full screen mode device.pressHome() tapl.getWorkspace() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } teardown { testApp.exit(wmHelper) } transitions { testApp.launchViaIntent(wmHelper) } diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index 7343ba1c1ce7..e60764f137af 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.content.pm.ServiceInfo +import android.hardware.input.KeyboardLayoutSelectionResult import android.hardware.input.IInputManager import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal @@ -525,13 +526,13 @@ class KeyboardLayoutManagerTests { keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, ENGLISH_UK_LAYOUT_DESCRIPTOR ) - val keyboardLayout = + assertEquals( + "Default UI: getKeyboardLayoutForInputDevice API should always return " + + "KeyboardLayoutSelectionResult.FAILED", + KeyboardLayoutSelectionResult.FAILED, keyboardLayoutManager.getKeyboardLayoutForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype ) - assertNull( - "Default UI: getKeyboardLayoutForInputDevice API should always return null", - keyboardLayout ) } } @@ -545,12 +546,14 @@ class KeyboardLayoutManagerTests { keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, ENGLISH_UK_LAYOUT_DESCRIPTOR ) - assertEquals( - "New UI: getKeyboardLayoutForInputDevice API should return the set layout", - ENGLISH_UK_LAYOUT_DESCRIPTOR, + var result = keyboardLayoutManager.getKeyboardLayoutForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype ) + assertEquals( + "New UI: getKeyboardLayoutForInputDevice API should return the set layout", + ENGLISH_UK_LAYOUT_DESCRIPTOR, + result.layoutDescriptor ) // This should replace previously set layout @@ -558,12 +561,14 @@ class KeyboardLayoutManagerTests { keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, ENGLISH_US_LAYOUT_DESCRIPTOR ) - assertEquals( - "New UI: getKeyboardLayoutForInputDevice API should return the last set layout", - ENGLISH_US_LAYOUT_DESCRIPTOR, + result = keyboardLayoutManager.getKeyboardLayoutForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype ) + assertEquals( + "New UI: getKeyboardLayoutForInputDevice API should return the last set layout", + ENGLISH_US_LAYOUT_DESCRIPTOR, + result.layoutDescriptor ) } } @@ -734,17 +739,20 @@ class KeyboardLayoutManagerTests { createImeSubtypeForLanguageTag("ru"), createLayoutDescriptor("keyboard_layout_russian") ) - assertNull( - "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " + - "layout available", + assertEquals( + "New UI: getDefaultKeyboardLayoutForInputDevice should return " + + "KeyboardLayoutSelectionResult.FAILED when no layout available", + KeyboardLayoutSelectionResult.FAILED, keyboardLayoutManager.getKeyboardLayoutForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, createImeSubtypeForLanguageTag("it") ) ) - assertNull( - "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " + - "layout for script code is available", + assertEquals( + "New UI: getDefaultKeyboardLayoutForInputDevice should return " + + "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" + + "available", + KeyboardLayoutSelectionResult.FAILED, keyboardLayoutManager.getKeyboardLayoutForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, createImeSubtypeForLanguageTag("en-Deva") @@ -811,8 +819,10 @@ class KeyboardLayoutManagerTests { createImeSubtypeForLanguageTagAndLayoutType("ru", ""), createLayoutDescriptor("keyboard_layout_russian") ) - assertNull("New UI: getDefaultKeyboardLayoutForInputDevice should return null when " + - "no layout for script code is available", + assertEquals("New UI: getDefaultKeyboardLayoutForInputDevice should return " + + "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" + + "available", + KeyboardLayoutSelectionResult.FAILED, keyboardLayoutManager.getKeyboardLayoutForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "") @@ -865,14 +875,16 @@ class KeyboardLayoutManagerTests { ArgumentMatchers.anyBoolean(), ArgumentMatchers.eq(keyboardDevice.vendorId), ArgumentMatchers.eq(keyboardDevice.productId), - ArgumentMatchers.eq(createByteArray( + ArgumentMatchers.eq( + createByteArray( KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, LAYOUT_TYPE_DEFAULT, GERMAN_LAYOUT_NAME, - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, "de-Latn", - LAYOUT_TYPE_QWERTZ), + LAYOUT_TYPE_QWERTZ ), + ), ArgumentMatchers.eq(keyboardDevice.deviceBus), ) } @@ -893,13 +905,16 @@ class KeyboardLayoutManagerTests { ArgumentMatchers.anyBoolean(), ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId), ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId), - ArgumentMatchers.eq(createByteArray( + ArgumentMatchers.eq( + createByteArray( "en", LAYOUT_TYPE_QWERTY, ENGLISH_US_LAYOUT_NAME, - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE, "de-Latn", - LAYOUT_TYPE_QWERTZ)), + LAYOUT_TYPE_QWERTZ + ) + ), ArgumentMatchers.eq(keyboardDevice.deviceBus), ) } @@ -918,14 +933,16 @@ class KeyboardLayoutManagerTests { ArgumentMatchers.anyBoolean(), ArgumentMatchers.eq(keyboardDevice.vendorId), ArgumentMatchers.eq(keyboardDevice.productId), - ArgumentMatchers.eq(createByteArray( + ArgumentMatchers.eq( + createByteArray( KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, LAYOUT_TYPE_DEFAULT, "Default", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT, KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, - LAYOUT_TYPE_DEFAULT), + LAYOUT_TYPE_DEFAULT ), + ), ArgumentMatchers.eq(keyboardDevice.deviceBus), ) } @@ -998,12 +1015,13 @@ class KeyboardLayoutManagerTests { imeSubtype: InputMethodSubtype, expectedLayout: String ) { + val result = keyboardLayoutManager.getKeyboardLayoutForInputDevice( + device.identifier, USER_ID, imeInfo, imeSubtype + ) assertEquals( "New UI: getDefaultKeyboardLayoutForInputDevice should return $expectedLayout", expectedLayout, - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - device.identifier, USER_ID, imeInfo, imeSubtype - ) + result.layoutDescriptor ) } diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt index 89a47b9b736a..0615941eda09 100644 --- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt @@ -17,6 +17,7 @@ package com.android.server.input import android.hardware.input.KeyboardLayout +import android.hardware.input.KeyboardLayoutSelectionResult import android.icu.util.ULocale import android.platform.test.annotations.Presubmit import android.view.InputDevice @@ -120,15 +121,15 @@ class KeyboardMetricsCollectorTests { val event = builder.addLayoutSelection( createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"), "English(US)(Qwerty)", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD ).addLayoutSelection( createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"), null, // Default layout type - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER ).addLayoutSelection( createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"), "German", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE ).setIsFirstTimeConfiguration(true).build() assertEquals( @@ -158,7 +159,7 @@ class KeyboardMetricsCollectorTests { "de-CH", KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"), "English(US)(Qwerty)", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, "en-US", KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"), ) @@ -167,7 +168,7 @@ class KeyboardMetricsCollectorTests { "de-CH", KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"), KeyboardMetricsCollector.DEFAULT_LAYOUT_NAME, - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER, "en-US", KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"), ) @@ -176,7 +177,7 @@ class KeyboardMetricsCollectorTests { "de-CH", KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"), "German", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE, "en-US", KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"), ) @@ -197,7 +198,7 @@ class KeyboardMetricsCollectorTests { val event = builder.addLayoutSelection( createImeSubtype(4, null, "qwerty"), // Default language tag "German", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE ).build() assertExpectedLayoutConfiguration( @@ -205,7 +206,7 @@ class KeyboardMetricsCollectorTests { KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"), "German", - KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE, KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"), ) diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index b054a57e85af..b4e2758f4abe 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -15,12 +15,7 @@ // package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], + default_applicable_licenses: ["Android-Apache-2.0"], } toolSources = [ diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk deleted file mode 100644 index 15ae2baa73df..000000000000 --- a/tools/aapt2/Android.mk +++ /dev/null @@ -1,4 +0,0 @@ -include $(CLEAR_VARS) -aapt2_results := ./out/soong/.intermediates/frameworks/base/tools/aapt2/aapt2_results -$(call declare-1p-target,$(aapt2_results)) -aapt2_results := |