diff options
444 files changed, 9292 insertions, 3147 deletions
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java index bb9f13f1712c..5cebf8d91cfc 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java @@ -128,7 +128,7 @@ class BlobStoreConfig { */ public static final String KEY_USE_REVOCABLE_FD_FOR_READS = "use_revocable_fd_for_reads"; - public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = true; + public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = false; public static boolean USE_REVOCABLE_FD_FOR_READS = DEFAULT_USE_REVOCABLE_FD_FOR_READS; diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index aeed1885dae5..36ccaf9c6fb8 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1120,6 +1120,10 @@ public class AppStandbyController implements AppStandbyInternal { if (isDeviceProvisioningPackage(packageName)) { return STANDBY_BUCKET_EXEMPTED; } + + if (mInjector.isWellbeingPackage(packageName)) { + return STANDBY_BUCKET_WORKING_SET; + } } // Check this last, as it can be the most expensive check @@ -1929,6 +1933,7 @@ public class AppStandbyController implements AppStandbyInternal { */ @GuardedBy("mPowerWhitelistedApps") private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>(); + private String mWellbeingApp = null; Injector(Context context, Looper looper) { mContext = context; @@ -1962,6 +1967,9 @@ public class AppStandbyController implements AppStandbyInternal { if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) { mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR; } + + final PackageManager packageManager = mContext.getPackageManager(); + mWellbeingApp = packageManager.getWellbeingPackageName(); } mBootPhase = phase; } @@ -2006,6 +2014,14 @@ public class AppStandbyController implements AppStandbyInternal { } } + /** + * Returns {@code true} if the supplied package is the wellbeing app. Otherwise, + * returns {@code false}. + */ + boolean isWellbeingPackage(String packageName) { + return mWellbeingApp != null && mWellbeingApp.equals(packageName); + } + void updatePowerWhitelistCache() { try { // Don't call out to DeviceIdleController with the lock held. diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 7d0d97917a4b..285eb4ac150e 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3863,6 +3863,17 @@ message LmkStateChanged { * system/core/lmkd/lmkd.c */ message LmkKillOccurred { + enum Reason { + UNKNOWN = 0; + PRESSURE_AFTER_KILL = 1; + NOT_RESPONDING = 2; + LOW_SWAP_AND_THRASHING = 3; + LOW_MEM_AND_SWAP = 4; + LOW_MEM_AND_THRASHING = 5; + DIRECT_RECL_AND_THRASHING = 6; + LOW_MEM_AND_SWAP_UTIL = 7; + } + // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -3892,6 +3903,15 @@ message LmkKillOccurred { // Min oom adj score considered by lmkd. optional int32 min_oom_score = 10; + + // Free physical memory on device at LMK time. + optional int32 free_mem_kb = 11; + + // Free swap on device at LMK time. + optional int32 free_swap_kb = 12; + + // What triggered the LMK event. + optional Reason reason = 13; } /* diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8d64661ef8a6..b47d44d60790 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3250,12 +3250,6 @@ public final class ActivityThread extends ClientTransactionHandler { sendMessage(H.CLEAN_UP_CONTEXT, cci); } - @Override - public void handleFixedRotationAdjustments(@NonNull IBinder token, - @Nullable FixedRotationAdjustments fixedRotationAdjustments) { - handleFixedRotationAdjustments(token, fixedRotationAdjustments, null /* overrideConfig */); - } - /** * Applies the rotation adjustments to override display information in resources belong to the * provided token. If the token is activity token, the adjustments also apply to application @@ -3265,51 +3259,39 @@ public final class ActivityThread extends ClientTransactionHandler { * @param fixedRotationAdjustments The information to override the display adjustments of * corresponding resources. If it is null, the exiting override * will be cleared. - * @param overrideConfig The override configuration of activity. It is used to override - * application configuration. If it is non-null, it means the token is - * confirmed as activity token. Especially when launching new activity, - * {@link #mActivities} hasn't put the new token. */ - private void handleFixedRotationAdjustments(@NonNull IBinder token, - @Nullable FixedRotationAdjustments fixedRotationAdjustments, - @Nullable Configuration overrideConfig) { - // The element of application configuration override is set only if the application - // adjustments are needed, because activity already has its own override configuration. - final Configuration[] appConfigOverride; - final Consumer<DisplayAdjustments> override; - if (fixedRotationAdjustments != null) { - appConfigOverride = new Configuration[1]; - override = displayAdjustments -> { - displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments); - if (appConfigOverride[0] != null) { - displayAdjustments.getConfiguration().updateFrom(appConfigOverride[0]); - } - }; - } else { - appConfigOverride = null; - override = null; - } + @Override + public void handleFixedRotationAdjustments(@NonNull IBinder token, + @Nullable FixedRotationAdjustments fixedRotationAdjustments) { + final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null + ? displayAdjustments -> displayAdjustments + .setFixedRotationAdjustments(fixedRotationAdjustments) + : null; if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) { // No resources are associated with the token. return; } - if (overrideConfig == null) { - final ActivityClientRecord r = mActivities.get(token); - if (r == null) { - // It is not an activity token. Nothing to do for application. - return; - } - overrideConfig = r.overrideConfig; - } - if (appConfigOverride != null) { - appConfigOverride[0] = overrideConfig; + if (mActivities.get(token) == null) { + // Nothing to do for application if it is not an activity token. + return; } - // Apply the last override to application resources for compatibility. Because the Resources - // of Display can be from application, e.g. - // applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId) - // and the deprecated usage: - // applicationContext.getSystemService(WindowManager.class).getDefaultDisplay(); + overrideApplicationDisplayAdjustments(token, override); + } + + /** + * Applies the last override to application resources for compatibility. Because the Resources + * of Display can be from application, e.g. + * applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId) + * and the deprecated usage: + * applicationContext.getSystemService(WindowManager.class).getDefaultDisplay(); + * + * @param token The owner and target of the override. + * @param override The display adjustments override for application resources. If it is null, + * the override of the token will be removed and pop the last one to use. + */ + private void overrideApplicationDisplayAdjustments(@NonNull IBinder token, + @Nullable Consumer<DisplayAdjustments> override) { final Consumer<DisplayAdjustments> appOverride; if (mActiveRotationAdjustments == null) { mActiveRotationAdjustments = new ArrayList<>(2); @@ -3542,8 +3524,13 @@ public final class ActivityThread extends ClientTransactionHandler { // The rotation adjustments must be applied before creating the activity, so the activity // can get the adjusted display info during creation. if (r.mPendingFixedRotationAdjustments != null) { - handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments, - r.overrideConfig); + // The adjustments should have been set by handleLaunchActivity, so the last one is the + // override for activity resources. + if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) { + mResourcesManager.overrideTokenDisplayAdjustments(r.token, + mActiveRotationAdjustments.get( + mActiveRotationAdjustments.size() - 1).second); + } r.mPendingFixedRotationAdjustments = null; } @@ -3582,6 +3569,13 @@ public final class ActivityThread extends ClientTransactionHandler { mProfiler.startProfiling(); } + if (r.mPendingFixedRotationAdjustments != null) { + // The rotation adjustments must be applied before handling configuration, so process + // level display metrics can be adjusted. + overrideApplicationDisplayAdjustments(r.token, adjustments -> + adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments)); + } + // Make sure we are running with the most recent config. handleConfigurationChanged(null, null); @@ -5777,7 +5771,15 @@ public final class ActivityThread extends ClientTransactionHandler { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: " + config); - mResourcesManager.applyConfigurationToResourcesLocked(config, compat); + final Resources appResources = mInitialApplication.getResources(); + if (appResources.hasOverrideDisplayAdjustments()) { + // The value of Display#getRealSize will be adjusted by FixedRotationAdjustments, + // but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated + // configuration also needs to set to the adjustments for consistency. + appResources.getDisplayAdjustments().getConfiguration().updateFrom(config); + } + mResourcesManager.applyConfigurationToResourcesLocked(config, compat, + appResources.getDisplayAdjustments()); updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(), mResourcesManager.getConfiguration().getLocales()); @@ -7390,7 +7392,8 @@ public final class ActivityThread extends ClientTransactionHandler { // We need to apply this change to the resources immediately, because upon returning // the view hierarchy will be informed about it. if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig, - null /* compat */)) { + null /* compat */, + mInitialApplication.getResources().getDisplayAdjustments())) { updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(), mResourcesManager.getConfiguration().getLocales()); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index c51a84649a07..7effbb3f7c84 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -187,16 +187,6 @@ class ContextImpl extends Context { private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache"; /** - * Special intent extra that critical system apps can use to hide the notification for a - * foreground service. This extra should be placed in the intent passed into {@link - * #startForegroundService(Intent)}. - * - * @hide - */ - private static final String EXTRA_HIDDEN_FOREGROUND_SERVICE = - "android.intent.extra.HIDDEN_FOREGROUND_SERVICE"; - - /** * Map from package name, to preference name, to cached preferences. */ @GuardedBy("ContextImpl.class") @@ -1707,12 +1697,9 @@ class ContextImpl extends Context { try { validateServiceIntent(service); service.prepareToLeaveProcess(this); - final boolean hideForegroundNotification = requireForeground - && service.getBooleanExtra(EXTRA_HIDDEN_FOREGROUND_SERVICE, false); ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, - hideForegroundNotification, getOpPackageName(), getAttributionTag(), user.getIdentifier()); if (cn != null) { if (cn.getPackageName().equals("!")) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f98e26338063..2fe7eea3b51f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -155,8 +155,7 @@ interface IActivityManager { boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta); PendingIntent getRunningServiceControlPanel(in ComponentName service); ComponentName startService(in IApplicationThread caller, in Intent service, - in String resolvedType, boolean requireForeground, - boolean hideForegroundNotification, in String callingPackage, + in String resolvedType, boolean requireForeground, in String callingPackage, in String callingFeatureId, int userId); @UnsupportedAppUsage int stopService(in IApplicationThread caller, in Intent service, diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b95a402013ec..c273cf08d03b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -207,7 +207,7 @@ public class Notification implements Parcelable * <p> * Avoids spamming the system with overly large strings such as full e-mails. */ - private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024; + private static final int MAX_CHARSEQUENCE_LENGTH = 1024; /** * Maximum entries of reply text that are accepted by Builder and friends. @@ -5151,10 +5151,18 @@ public class Notification implements Parcelable bindHeaderChronometerAndTime(contentView, p); bindProfileBadge(contentView, p); bindAlertedIcon(contentView, p); + bindActivePermissions(contentView, p); bindExpandButton(contentView, p); mN.mUsesStandardHeader = true; } + private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) { + int color = getNeutralColor(p); + contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP); + contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP); + contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP); + } + private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) { int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p); contentView.setDrawableTint(R.id.expand_button, false, color, @@ -7821,7 +7829,7 @@ public class Notification implements Parcelable */ public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender, boolean remoteInputHistory) { - mText = text; + mText = safeCharSequence(text); mTimestamp = timestamp; mSender = sender; mRemoteInputHistory = remoteInputHistory; @@ -7935,7 +7943,7 @@ public class Notification implements Parcelable bundle.putLong(KEY_TIMESTAMP, mTimestamp); if (mSender != null) { // Legacy listeners need this - bundle.putCharSequence(KEY_SENDER, mSender.getName()); + bundle.putCharSequence(KEY_SENDER, safeCharSequence(mSender.getName())); bundle.putParcelable(KEY_SENDER_PERSON, mSender); } if (mDataMimeType != null) { diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 4cba6ea74955..273336d8189a 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -40,7 +40,6 @@ import android.os.Trace; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; -import android.util.LruCache; import android.util.Pair; import android.util.Slog; import android.view.Display; @@ -63,7 +62,6 @@ import java.util.List; import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Consumer; -import java.util.function.Predicate; /** @hide */ public class ResourcesManager { @@ -130,17 +128,30 @@ public class ResourcesManager { } } - private static final boolean ENABLE_APK_ASSETS_CACHE = false; - /** - * The ApkAssets we are caching and intend to hold strong references to. + * Loads {@link ApkAssets} and caches them to prevent their garbage collection while the + * instance is alive and reachable. */ - private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = - (ENABLE_APK_ASSETS_CACHE) ? new LruCache<>(3) : null; + private class ApkAssetsSupplier { + final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>(); + + /** + * Retrieves the {@link ApkAssets} corresponding to the specified key, caches the ApkAssets + * within this instance, and inserts the loaded ApkAssets into the {@link #mCachedApkAssets} + * cache. + */ + ApkAssets load(final ApkKey apkKey) throws IOException { + ApkAssets apkAssets = mLocalCache.get(apkKey); + if (apkAssets == null) { + apkAssets = loadApkAssets(apkKey); + mLocalCache.put(apkKey, apkAssets); + } + return apkAssets; + } + } /** - * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't - * in our LRU cache. Bonus resources :) + * The ApkAssets that are being referenced in the wild that we can reuse. */ private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>(); @@ -338,113 +349,116 @@ public class ResourcesManager { return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; } - private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay) - throws IOException { - final ApkKey newKey = new ApkKey(path, sharedLib, overlay); - ApkAssets apkAssets = null; - if (mLoadedApkAssets != null) { - apkAssets = mLoadedApkAssets.get(newKey); - if (apkAssets != null && apkAssets.isUpToDate()) { - return apkAssets; - } - } + private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException { + ApkAssets apkAssets; // Optimistically check if this ApkAssets exists somewhere else. - final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey); - if (apkAssetsRef != null) { - apkAssets = apkAssetsRef.get(); - if (apkAssets != null && apkAssets.isUpToDate()) { - if (mLoadedApkAssets != null) { - mLoadedApkAssets.put(newKey, apkAssets); + synchronized (this) { + final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key); + if (apkAssetsRef != null) { + apkAssets = apkAssetsRef.get(); + if (apkAssets != null && apkAssets.isUpToDate()) { + return apkAssets; + } else { + // Clean up the reference. + mCachedApkAssets.remove(key); } - - return apkAssets; - } else { - // Clean up the reference. - mCachedApkAssets.remove(newKey); } } // We must load this from disk. - if (overlay) { - apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), 0 /*flags*/); + if (key.overlay) { + apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path), + 0 /*flags*/); } else { - apkAssets = ApkAssets.loadFromPath(path, sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0); + apkAssets = ApkAssets.loadFromPath(key.path, + key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0); } - if (mLoadedApkAssets != null) { - mLoadedApkAssets.put(newKey, apkAssets); + synchronized (this) { + mCachedApkAssets.put(key, new WeakReference<>(apkAssets)); } - mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets)); return apkAssets; } /** - * Creates an AssetManager from the paths within the ResourcesKey. - * - * This can be overridden in tests so as to avoid creating a real AssetManager with - * real APK paths. - * @param key The key containing the resource paths to add to the AssetManager. - * @return a new AssetManager. - */ - @VisibleForTesting - @UnsupportedAppUsage - protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { - final AssetManager.Builder builder = new AssetManager.Builder(); + * Retrieves a list of apk keys representing the ApkAssets that should be loaded for + * AssetManagers mapped to the {@param key}. + */ + private static @NonNull ArrayList<ApkKey> extractApkKeys(@NonNull final ResourcesKey key) { + final ArrayList<ApkKey> apkKeys = new ArrayList<>(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (key.mResDir != null) { - try { - builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/, - false /*overlay*/)); - } catch (IOException e) { - Log.e(TAG, "failed to add asset path " + key.mResDir); - return null; - } + apkKeys.add(new ApkKey(key.mResDir, false /*sharedLib*/, false /*overlay*/)); } if (key.mSplitResDirs != null) { for (final String splitResDir : key.mSplitResDirs) { - try { - builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/, - false /*overlay*/)); - } catch (IOException e) { - Log.e(TAG, "failed to add split asset path " + splitResDir); - return null; - } + apkKeys.add(new ApkKey(splitResDir, false /*sharedLib*/, false /*overlay*/)); } } if (key.mLibDirs != null) { for (final String libDir : key.mLibDirs) { + // Avoid opening files we know do not have resources, like code-only .jar files. if (libDir.endsWith(".apk")) { - // Avoid opening files we know do not have resources, - // like code-only .jar files. - try { - builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/, - false /*overlay*/)); - } catch (IOException e) { - Log.w(TAG, "Asset path '" + libDir + - "' does not exist or contains no resources."); - - // continue. - } + apkKeys.add(new ApkKey(libDir, true /*sharedLib*/, false /*overlay*/)); } } } if (key.mOverlayDirs != null) { for (final String idmapPath : key.mOverlayDirs) { - try { - builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, - true /*overlay*/)); - } catch (IOException e) { - Log.w(TAG, "failed to add overlay path " + idmapPath); + apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/)); + } + } - // continue. + return apkKeys; + } + + /** + * Creates an AssetManager from the paths within the ResourcesKey. + * + * This can be overridden in tests so as to avoid creating a real AssetManager with + * real APK paths. + * @param key The key containing the resource paths to add to the AssetManager. + * @return a new AssetManager. + */ + @VisibleForTesting + @UnsupportedAppUsage + protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { + return createAssetManager(key, /* apkSupplier */ null); + } + + /** + * Variant of {@link #createAssetManager(ResourcesKey)} that attempts to load ApkAssets + * from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using + * {@link #loadApkAssets(ApkKey)}. + */ + private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key, + @Nullable ApkAssetsSupplier apkSupplier) { + final AssetManager.Builder builder = new AssetManager.Builder(); + + final ArrayList<ApkKey> apkKeys = extractApkKeys(key); + for (int i = 0, n = apkKeys.size(); i < n; i++) { + final ApkKey apkKey = apkKeys.get(i); + try { + builder.addApkAssets( + (apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey)); + } catch (IOException e) { + if (apkKey.overlay) { + Log.w(TAG, String.format("failed to add overlay path '%s'", apkKey.path), e); + } else if (apkKey.sharedLib) { + Log.w(TAG, String.format( + "asset path '%s' does not exist or contains no resources", + apkKey.path), e); + } else { + Log.e(TAG, String.format("failed to add asset path '%s'", apkKey.path), e); + return null; } } } @@ -481,24 +495,6 @@ public class ResourcesManager { pw.println("ResourcesManager:"); pw.increaseIndent(); - if (mLoadedApkAssets != null) { - pw.print("cached apks: total="); - pw.print(mLoadedApkAssets.size()); - pw.print(" created="); - pw.print(mLoadedApkAssets.createCount()); - pw.print(" evicted="); - pw.print(mLoadedApkAssets.evictionCount()); - pw.print(" hit="); - pw.print(mLoadedApkAssets.hitCount()); - pw.print(" miss="); - pw.print(mLoadedApkAssets.missCount()); - pw.print(" max="); - pw.print(mLoadedApkAssets.maxSize()); - } else { - pw.print("cached apks: 0 [cache disabled]"); - } - pw.println(); - pw.print("total apks: "); pw.println(countLiveReferences(mCachedApkAssets.values())); @@ -534,11 +530,12 @@ public class ResourcesManager { return config; } - private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) { + private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key, + @Nullable ApkAssetsSupplier apkSupplier) { final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration); daj.setCompatibilityInfo(key.mCompatInfo); - final AssetManager assets = createAssetManager(key); + final AssetManager assets = createAssetManager(key, apkSupplier); if (assets == null) { return null; } @@ -576,9 +573,18 @@ public class ResourcesManager { */ private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked( @NonNull ResourcesKey key) { + return findOrCreateResourcesImplForKeyLocked(key, /* apkSupplier */ null); + } + + /** + * Variant of {@link #findOrCreateResourcesImplForKeyLocked(ResourcesKey)} that attempts to + * load ApkAssets from a {@link ApkAssetsSupplier} when creating a new ResourcesImpl. + */ + private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked( + @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) { ResourcesImpl impl = findResourcesImplForKeyLocked(key); if (impl == null) { - impl = createResourcesImpl(key); + impl = createResourcesImpl(key, apkSupplier); if (impl != null) { mResourceImpls.put(key, new WeakReference<>(impl)); } @@ -767,7 +773,7 @@ public class ResourcesManager { } // Now request an actual Resources object. - return createResources(token, key, classLoader); + return createResources(token, key, classLoader, /* apkSupplier */ null); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } @@ -811,18 +817,45 @@ public class ResourcesManager { } /** + * Creates an {@link ApkAssetsSupplier} and loads all the ApkAssets required by the {@param key} + * into the supplier. This should be done while the lock is not held to prevent performing I/O + * while holding the lock. + */ + private @NonNull ApkAssetsSupplier createApkAssetsSupplierNotLocked(@NonNull ResourcesKey key) { + Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, + "ResourcesManager#createApkAssetsSupplierNotLocked"); + try { + final ApkAssetsSupplier supplier = new ApkAssetsSupplier(); + final ArrayList<ApkKey> apkKeys = extractApkKeys(key); + for (int i = 0, n = apkKeys.size(); i < n; i++) { + final ApkKey apkKey = apkKeys.get(i); + try { + supplier.load(apkKey); + } catch (IOException e) { + Log.w(TAG, String.format("failed to preload asset path '%s'", apkKey.path), e); + } + } + return supplier; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); + } + } + + /** * Creates a Resources object set with a ResourcesImpl object matching the given key. * * @param activityToken The Activity this Resources object should be associated with. * @param key The key describing the parameters of the ResourcesImpl object. * @param classLoader The classloader to use for the Resources object. * If null, {@link ClassLoader#getSystemClassLoader()} is used. + * @param apkSupplier The apk assets supplier to use when creating a new ResourcesImpl object. * @return A Resources object that gets updated when * {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)} * is called. */ private @Nullable Resources createResources(@Nullable IBinder activityToken, - @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) { + @NonNull ResourcesKey key, @NonNull ClassLoader classLoader, + @Nullable ApkAssetsSupplier apkSupplier) { synchronized (this) { if (DEBUG) { Throwable here = new Throwable(); @@ -830,7 +863,7 @@ public class ResourcesManager { Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here); } - ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key); + ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier); if (resourcesImpl == null) { return null; } @@ -899,7 +932,10 @@ public class ResourcesManager { rebaseKeyForActivity(activityToken, key); } - return createResources(activityToken, key, classLoader); + // Preload the ApkAssets required by the key to prevent performing heavy I/O while the + // ResourcesManager lock is held. + final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key); + return createResources(activityToken, key, classLoader, assetsSupplier); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } @@ -970,7 +1006,13 @@ public class ResourcesManager { final ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig, overrideConfig, displayId); if (newKey != null) { - updateActivityResources(resources, newKey, false); + final ResourcesImpl resourcesImpl = + findOrCreateResourcesImplForKeyLocked(newKey); + if (resourcesImpl != null && resourcesImpl != resources.getImpl()) { + // Set the ResourcesImpl, updating it for all users of this Resources + // object. + resources.setImpl(resourcesImpl); + } } } } @@ -1025,34 +1067,22 @@ public class ResourcesManager { return newKey; } - private void updateActivityResources(Resources resources, ResourcesKey newKey, - boolean hasLoader) { - final ResourcesImpl resourcesImpl; - - if (hasLoader) { - // Loaders always get new Impls because they cannot be shared - resourcesImpl = createResourcesImpl(newKey); - } else { - resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey); - } - - if (resourcesImpl != null && resourcesImpl != resources.getImpl()) { - // Set the ResourcesImpl, updating it for all users of this Resources - // object. - resources.setImpl(resourcesImpl); - } - } - @TestApi public final boolean applyConfigurationToResources(@NonNull Configuration config, @Nullable CompatibilityInfo compat) { synchronized(this) { - return applyConfigurationToResourcesLocked(config, compat); + return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */); } } public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config, - @Nullable CompatibilityInfo compat) { + @Nullable CompatibilityInfo compat) { + return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */); + } + + /** Applies the global configuration to the managed resources. */ + public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config, + @Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) { try { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#applyConfigurationToResourcesLocked"); @@ -1076,6 +1106,11 @@ public class ResourcesManager { | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; } + if (adjustments != null) { + // Currently the only case where the adjustment takes effect is to simulate placing + // an app in a rotated display. + adjustments.adjustGlobalAppMetrics(defaultDisplayMetrics); + } Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); ApplicationPackageManager.configurationChanged(); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 7c6eff143724..06d1b74abc86 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -510,6 +510,9 @@ public class UiModeManager { } /** + * Activating night mode for the current user + * + * @return {@code true} if the change is successful * @hide */ public boolean setNightModeActivated(boolean active) { diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index d2a15357aa1f..735980beebb1 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; - + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; /** @hide */ @@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; @@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; @@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @ChannelMode int channelMode, long codecSpecific1, @@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; @@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } @@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } @@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 1e394b830d51..7b567b4098e7 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index bd02210259b8..31c77eeb5424 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -963,7 +963,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { /** @hide */ public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2; /** @hide */ - public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3; + public static final int LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED = 3; /** @hide */ public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) { @@ -974,8 +974,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { return "LOCK_TASK_LAUNCH_MODE_NEVER"; case LOCK_TASK_LAUNCH_MODE_ALWAYS: return "LOCK_TASK_LAUNCH_MODE_ALWAYS"; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED"; + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + return "LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED"; default: return "unknown=" + lockTaskLaunchMode; } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index f257326904fd..2138f53e9f54 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -785,4 +785,6 @@ interface IPackageManager { List<String> getMimeGroup(String packageName, String group); boolean isAutoRevokeWhitelisted(String packageName); + + void grantImplicitAccess(int queryingUid, String visibleAuthority); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ea4a2a0b8c35..8a7214db31eb 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -8011,6 +8011,20 @@ public abstract class PackageManager { "getMimeGroup not implemented in subclass"); } + /** + * Grants implicit visibility of the package that provides an authority to a querying UID. + * + * @throws SecurityException when called by a package other than the contacts provider + * @hide + */ + public void grantImplicitAccess(int queryingUid, String visibleAuthority) { + try { + ActivityThread.getPackageManager().grantImplicitAccess(queryingUid, visibleAuthority); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + // Some of the flags don't affect the query result, but let's be conservative and cache // each combination of flags separately. diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index aca5b458a2d6..08b23b04f2ae 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -221,6 +221,14 @@ public class UserInfo implements Parcelable { public boolean preCreated; /** + * When {@code true}, it indicates this user was created by converting a {@link #preCreated} + * user. + * + * <p><b>NOTE: </b>only used for debugging purposes, it's not set when marshalled to a parcel. + */ + public boolean convertedFromPreCreated; + + /** * Creates a UserInfo whose user type is determined automatically by the flags according to * {@link #getDefaultUserType}; can only be used for user types handled there. */ @@ -413,6 +421,7 @@ public class UserInfo implements Parcelable { lastLoggedInFingerprint = orig.lastLoggedInFingerprint; partial = orig.partial; preCreated = orig.preCreated; + convertedFromPreCreated = orig.convertedFromPreCreated; profileGroupId = orig.profileGroupId; restrictedProfileParentId = orig.restrictedProfileParentId; guestToRemove = orig.guestToRemove; @@ -440,6 +449,7 @@ public class UserInfo implements Parcelable { + ", type=" + userType + ", flags=" + flagsToString(flags) + (preCreated ? " (pre-created)" : "") + + (convertedFromPreCreated ? " (converted)" : "") + (partial ? " (partial)" : "") + "]"; } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4f0c84e586a2..e0195e4eafc1 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -16,11 +16,11 @@ package android.inputmethodservice; +import static android.graphics.Color.TRANSPARENT; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.WindowInsets.Type.navigationBars; -import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -69,7 +69,6 @@ import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; import android.view.Window; -import android.view.WindowInsets; import android.view.WindowInsets.Side; import android.view.WindowManager; import android.view.animation.AnimationUtils; @@ -1203,25 +1202,22 @@ public class InputMethodService extends AbstractInputMethodService { Context.LAYOUT_INFLATER_SERVICE); mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); - mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars()); + mWindow.getWindow().getAttributes().setFitInsetsTypes(navigationBars()); mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM); mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true); - // IME layout should always be inset by navigation bar, no matter its current visibility, - // unless automotive requests it. Automotive devices may request the navigation bar to be - // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard) - // in order to maximize the visible screen real estate. When this happens, the IME window - // should animate from the bottom of the screen to reduce the jank that happens from the - // lack of synchronization between the bottom system window and the IME window. + // Our window will extend into the status bar area no matter the bar is visible or not. + // We don't want the ColorView to be visible when status bar is shown. + mWindow.getWindow().setStatusBarColor(TRANSPARENT); + + // Automotive devices may request the navigation bar to be hidden when the IME shows up + // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible + // screen real estate. When this happens, the IME window should animate from the bottom of + // the screen to reduce the jank that happens from the lack of synchronization between the + // bottom system window and the IME window. if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) { mWindow.getWindow().setDecorFitsSystemWindows(false); } - mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener( - (v, insets) -> v.onApplyWindowInsets( - new WindowInsets.Builder(insets).setInsets( - navigationBars(), - insets.getInsetsIgnoringVisibility(navigationBars())) - .build())); // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set // by default (but IME developers can opt this out later if they want a new behavior). diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 49a1cb588a3e..018bb2c9f9b2 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -135,8 +135,8 @@ public class Build { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -1021,6 +1021,18 @@ public class Build { /** * R. + * + * <p>Applications targeting this or a later release will get these new changes in behavior. + * For more information about this release, see the + * <a href="/about/versions/11">Android 11 overview</a>.</p> + * <ul> + * <li><a href="/about/versions/11/behavior-changes-all">Behavior changes: all apps</a></li> + * <li><a href="/about/versions/11/behavior-changes-11">Behavior changes: Apps targeting + * Android 11</a></li> + * <li><a href="/about/versions/11/non-sdk-11">Updates to non-SDK interface restrictions + * in Android 11</a></li> + * </ul> + * */ public static final int R = 30; } diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index ab4bb0b9f2cd..9c0bc45a346e 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -25,6 +25,7 @@ import android.icu.util.ULocale; import com.android.internal.annotations.GuardedBy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -151,18 +152,18 @@ public final class LocaleList implements Parcelable { /** * Creates a new {@link LocaleList}. * + * If two or more same locales are passed, the repeated locales will be dropped. * <p>For empty lists of {@link Locale} items it is better to use {@link #getEmptyLocaleList()}, * which returns a pre-constructed empty list.</p> * * @throws NullPointerException if any of the input locales is <code>null</code>. - * @throws IllegalArgumentException if any of the input locales repeat. */ public LocaleList(@NonNull Locale... list) { if (list.length == 0) { mList = sEmptyList; mStringRepresentation = ""; } else { - final Locale[] localeList = new Locale[list.length]; + final ArrayList<Locale> localeList = new ArrayList<>(); final HashSet<Locale> seenLocales = new HashSet<Locale>(); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < list.length; i++) { @@ -170,10 +171,10 @@ public final class LocaleList implements Parcelable { if (l == null) { throw new NullPointerException("list[" + i + "] is null"); } else if (seenLocales.contains(l)) { - throw new IllegalArgumentException("list[" + i + "] is a repetition"); + // Dropping duplicated locale entries. } else { final Locale localeClone = (Locale) l.clone(); - localeList[i] = localeClone; + localeList.add(localeClone); sb.append(localeClone.toLanguageTag()); if (i < list.length - 1) { sb.append(','); @@ -181,7 +182,7 @@ public final class LocaleList implements Parcelable { seenLocales.add(localeClone); } } - mList = localeList; + mList = localeList.toArray(new Locale[localeList.size()]); mStringRepresentation = sb.toString(); } } diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index e05991b33796..a79a1cfcd64f 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -118,7 +118,7 @@ public abstract class StorageManagerInternal { * affects them. */ public abstract void onAppOpsChanged(int code, int uid, - @Nullable String packageName, int mode); + @Nullable String packageName, int mode, int previousMode); /** * Asks the StorageManager to reset all state for the provided user; this will result diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 10ac27ca57fd..b07dabd4bb39 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8929,14 +8929,6 @@ public final class Settings { public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption"; /** - * Controls which packages are blocked from persisting in media controls when resumption is - * enabled. The list of packages is set by the user in the Settings app. - * @see Settings.Secure#MEDIA_CONTROLS_RESUME - * @hide - */ - public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; - - /** * Controls if window magnification is enabled. * @hide */ @@ -8988,6 +8980,22 @@ public final class Settings { }; /** + * How long Assistant handles have enabled in milliseconds. + * + * @hide + */ + public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS = + "reminder_exp_learning_time_elapsed"; + + /** + * How many times the Assistant has been triggered using the touch gesture. + * + * @hide + */ + public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT = + "reminder_exp_learning_event_count"; + + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. */ diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 2c66910529f1..718f2de33d5c 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3954,9 +3954,7 @@ public final class Telephony { public static final String APN_SET_ID = "apn_set_id"; /** - * Possible value for the {@link #APN_SET_ID} field. By default APNs will not belong to a - * set. If the user manually selects an APN without apn set id, there is no need to - * prioritize any specific APN set ids. + * Possible value for the {@link #APN_SET_ID} field. By default APNs are added to set 0. * <p>Type: INTEGER</p> * @hide */ @@ -3964,6 +3962,15 @@ public final class Telephony { public static final int NO_APN_SET_ID = 0; /** + * Possible value for the {@link #APN_SET_ID} field. + * APNs with MATCH_ALL_APN_SET_ID will be used regardless of any set ids of + * the selected APN. + * <p>Type: INTEGER</p> + * @hide + */ + public static final int MATCH_ALL_APN_SET_ID = -1; + + /** * A unique carrier id associated with this APN * {@see TelephonyManager#getSimCarrierId()} * <p>Type: STRING</p> diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java index c726bee9f402..7c01f7a8739a 100644 --- a/core/java/android/view/DisplayAdjustments.java +++ b/core/java/android/view/DisplayAdjustments.java @@ -130,14 +130,16 @@ public class DisplayAdjustments { w = metrics.noncompatWidthPixels; metrics.noncompatWidthPixels = metrics.noncompatHeightPixels; metrics.noncompatHeightPixels = w; + } - float x = metrics.xdpi; - metrics.xdpi = metrics.ydpi; - metrics.ydpi = x; - - x = metrics.noncompatXdpi; - metrics.noncompatXdpi = metrics.noncompatYdpi; - metrics.noncompatYdpi = x; + /** Adjusts global display metrics that is available to applications. */ + public void adjustGlobalAppMetrics(@NonNull DisplayMetrics metrics) { + final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments; + if (rotationAdjustments == null) { + return; + } + metrics.noncompatWidthPixels = metrics.widthPixels = rotationAdjustments.mAppWidth; + metrics.noncompatHeightPixels = metrics.heightPixels = rotationAdjustments.mAppHeight; } /** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */ @@ -178,7 +180,7 @@ public class DisplayAdjustments { /** * An application can be launched in different rotation than the real display. This class - * provides the information to adjust the values returned by {@link #Display}. + * provides the information to adjust the values returned by {@link Display}. * @hide */ public static class FixedRotationAdjustments implements Parcelable { @@ -186,12 +188,24 @@ public class DisplayAdjustments { @Surface.Rotation final int mRotation; + /** + * The rotated {@link DisplayInfo#appWidth}. The value cannot be simply swapped according + * to rotation because it minus the region of screen decorations. + */ + final int mAppWidth; + + /** The rotated {@link DisplayInfo#appHeight}. */ + final int mAppHeight; + /** Non-null if the device has cutout. */ @Nullable final DisplayCutout mRotatedDisplayCutout; - public FixedRotationAdjustments(@Surface.Rotation int rotation, DisplayCutout cutout) { + public FixedRotationAdjustments(@Surface.Rotation int rotation, int appWidth, int appHeight, + DisplayCutout cutout) { mRotation = rotation; + mAppWidth = appWidth; + mAppHeight = appHeight; mRotatedDisplayCutout = cutout; } @@ -199,6 +213,8 @@ public class DisplayAdjustments { public int hashCode() { int hash = 17; hash = hash * 31 + mRotation; + hash = hash * 31 + mAppWidth; + hash = hash * 31 + mAppHeight; hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout); return hash; } @@ -210,12 +226,14 @@ public class DisplayAdjustments { } final FixedRotationAdjustments other = (FixedRotationAdjustments) o; return mRotation == other.mRotation + && mAppWidth == other.mAppWidth && mAppHeight == other.mAppHeight && Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout); } @Override public String toString() { return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation) + + " appWidth=" + mAppWidth + " appHeight=" + mAppHeight + " cutout=" + mRotatedDisplayCutout + "}"; } @@ -227,12 +245,16 @@ public class DisplayAdjustments { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mRotation); + dest.writeInt(mAppWidth); + dest.writeInt(mAppHeight); dest.writeTypedObject( new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags); } private FixedRotationAdjustments(Parcel in) { mRotation = in.readInt(); + mAppWidth = in.readInt(); + mAppHeight = in.readInt(); final DisplayCutout.ParcelableWrapper cutoutWrapper = in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR); mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null; diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 593b37af26ad..81378831adbc 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -76,6 +76,10 @@ public class InsetsState implements Parcelable { ITYPE_BOTTOM_GESTURES, ITYPE_LEFT_GESTURES, ITYPE_RIGHT_GESTURES, + ITYPE_TOP_MANDATORY_GESTURES, + ITYPE_BOTTOM_MANDATORY_GESTURES, + ITYPE_LEFT_MANDATORY_GESTURES, + ITYPE_RIGHT_MANDATORY_GESTURES, ITYPE_TOP_TAPPABLE_ELEMENT, ITYPE_BOTTOM_TAPPABLE_ELEMENT, ITYPE_LEFT_DISPLAY_CUTOUT, @@ -104,20 +108,27 @@ public class InsetsState implements Parcelable { public static final int ITYPE_BOTTOM_GESTURES = 4; public static final int ITYPE_LEFT_GESTURES = 5; public static final int ITYPE_RIGHT_GESTURES = 6; - public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 7; - public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 8; - public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 9; - public static final int ITYPE_TOP_DISPLAY_CUTOUT = 10; - public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 11; - public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 12; + /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */ + public static final int ITYPE_TOP_MANDATORY_GESTURES = 7; + public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8; + public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; + public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10; + + public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 11; + public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 12; + + public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 13; + public static final int ITYPE_TOP_DISPLAY_CUTOUT = 14; + public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 15; + public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 16; /** Input method window. */ - public static final int ITYPE_IME = 13; + public static final int ITYPE_IME = 17; /** Additional system decorations inset type. */ - public static final int ITYPE_CLIMATE_BAR = 14; - public static final int ITYPE_EXTRA_NAVIGATION_BAR = 15; + public static final int ITYPE_CLIMATE_BAR = 18; + public static final int ITYPE_EXTRA_NAVIGATION_BAR = 19; static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR; public static final int SIZE = LAST_TYPE + 1; @@ -453,9 +464,11 @@ public class InsetsState implements Parcelable { final ArraySet<Integer> result = new ArraySet<>(); if ((types & Type.STATUS_BARS) != 0) { result.add(ITYPE_STATUS_BAR); + result.add(ITYPE_CLIMATE_BAR); } if ((types & Type.NAVIGATION_BARS) != 0) { result.add(ITYPE_NAVIGATION_BAR); + result.add(ITYPE_EXTRA_NAVIGATION_BAR); } if ((types & Type.CAPTION_BAR) != 0) { result.add(ITYPE_CAPTION_BAR); @@ -491,6 +504,10 @@ public class InsetsState implements Parcelable { return Type.IME; case ITYPE_TOP_GESTURES: case ITYPE_BOTTOM_GESTURES: + case ITYPE_TOP_MANDATORY_GESTURES: + case ITYPE_BOTTOM_MANDATORY_GESTURES: + case ITYPE_LEFT_MANDATORY_GESTURES: + case ITYPE_RIGHT_MANDATORY_GESTURES: return Type.MANDATORY_SYSTEM_GESTURES; case ITYPE_LEFT_GESTURES: case ITYPE_RIGHT_GESTURES: @@ -550,6 +567,14 @@ public class InsetsState implements Parcelable { return "ITYPE_LEFT_GESTURES"; case ITYPE_RIGHT_GESTURES: return "ITYPE_RIGHT_GESTURES"; + case ITYPE_TOP_MANDATORY_GESTURES: + return "ITYPE_TOP_MANDATORY_GESTURES"; + case ITYPE_BOTTOM_MANDATORY_GESTURES: + return "ITYPE_BOTTOM_MANDATORY_GESTURES"; + case ITYPE_LEFT_MANDATORY_GESTURES: + return "ITYPE_LEFT_MANDATORY_GESTURES"; + case ITYPE_RIGHT_MANDATORY_GESTURES: + return "ITYPE_RIGHT_MANDATORY_GESTURES"; case ITYPE_TOP_TAPPABLE_ELEMENT: return "ITYPE_TOP_TAPPABLE_ELEMENT"; case ITYPE_BOTTOM_TAPPABLE_ELEMENT: diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 7a467d6ad73f..0c50cb782c24 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -52,11 +52,13 @@ public class NotificationHeaderView extends ViewGroup { private View mHeaderText; private View mSecondaryHeaderText; private OnClickListener mExpandClickListener; + private OnClickListener mAppOpsListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); private LinearLayout mTransferChip; private NotificationExpandButton mExpandButton; private CachingIconView mIcon; private View mProfileBadge; + private View mAppOps; private boolean mExpanded; private boolean mShowExpandButtonAtEnd; private boolean mShowWorkBadgeAtEnd; @@ -113,6 +115,7 @@ public class NotificationHeaderView extends ViewGroup { mExpandButton = findViewById(com.android.internal.R.id.expand_button); mIcon = findViewById(com.android.internal.R.id.icon); mProfileBadge = findViewById(com.android.internal.R.id.profile_badge); + mAppOps = findViewById(com.android.internal.R.id.app_ops); } @Override @@ -140,6 +143,7 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge + || child == mAppOps || child == mTransferChip) { iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); } else { @@ -204,6 +208,7 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge + || child == mAppOps || child == mTransferChip) { if (end == getMeasuredWidth()) { layoutRight = end - mContentEndMargin; @@ -272,10 +277,22 @@ public class NotificationHeaderView extends ViewGroup { } private void updateTouchListener() { + if (mExpandClickListener == null && mAppOpsListener == null) { + setOnTouchListener(null); + return; + } setOnTouchListener(mTouchListener); mTouchListener.bindTouchRects(); } + /** + * Sets onclick listener for app ops icons. + */ + public void setAppOpsOnClickListener(OnClickListener l) { + mAppOpsListener = l; + updateTouchListener(); + } + @Override public void setOnClickListener(@Nullable OnClickListener l) { mExpandClickListener = l; @@ -363,6 +380,7 @@ public class NotificationHeaderView extends ViewGroup { private final ArrayList<Rect> mTouchRects = new ArrayList<>(); private Rect mExpandButtonRect; + private Rect mAppOpsRect; private int mTouchSlop; private boolean mTrackGesture; private float mDownX; @@ -375,6 +393,8 @@ public class NotificationHeaderView extends ViewGroup { mTouchRects.clear(); addRectAroundView(mIcon); mExpandButtonRect = addRectAroundView(mExpandButton); + mAppOpsRect = addRectAroundView(mAppOps); + setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps)); addWidthRect(); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -435,6 +455,11 @@ public class NotificationHeaderView extends ViewGroup { break; case MotionEvent.ACTION_UP: if (mTrackGesture) { + if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y) + || mAppOpsRect.contains((int) mDownX, (int) mDownY))) { + mAppOps.performClick(); + return true; + } mExpandButton.performClick(); } break; diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index ffeeb806ba54..8fd2f07f3f39 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -99,7 +99,7 @@ public class ViewConfiguration { * Defines the duration in milliseconds a user needs to hold down the * appropriate buttons (power + volume down) to trigger the screenshot chord. */ - private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 500; + private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 0; /** * Defines the duration in milliseconds a user needs to hold down the diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index bcf3b49c6644..3763728fa071 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -53,6 +53,9 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CO import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; @@ -1449,14 +1452,13 @@ public final class ViewRootImpl implements ViewParent, } // Don't lose the mode we last auto-computed. - if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + if ((attrs.softInputMode & SOFT_INPUT_MASK_ADJUST) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode - & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); + & ~SOFT_INPUT_MASK_ADJUST) | (oldSoftInputMode & SOFT_INPUT_MASK_ADJUST); } - if ((changes & LayoutParams.SOFT_INPUT_MODE_CHANGED) != 0) { + if (mWindowAttributes.softInputMode != oldSoftInputMode) { requestFitSystemWindows(); } @@ -1979,11 +1981,7 @@ public final class ViewRootImpl implements ViewParent, mCompatibleVisibilityInfo.globalVisibility = (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE) | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); - if (mDispatchedSystemUiVisibility != mCompatibleVisibilityInfo.globalVisibility) { - mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY); - mHandler.sendMessage(mHandler.obtainMessage( - MSG_DISPATCH_SYSTEM_UI_VISIBILITY, mCompatibleVisibilityInfo)); - } + dispatchDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo); if (mAttachInfo.mKeepScreenOn != oldScreenOn || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) { @@ -2037,9 +2035,30 @@ public final class ViewRootImpl implements ViewParent, info.globalVisibility |= systemUiFlag; info.localChanges &= ~systemUiFlag; } - if (mDispatchedSystemUiVisibility != info.globalVisibility) { + dispatchDispatchSystemUiVisibilityChanged(info); + } + + /** + * If the system is forcing showing any system bar, the legacy low profile flag should be + * cleared for compatibility. + * + * @param showTypes {@link InsetsType types} shown by the system. + * @param fromIme {@code true} if the invocation is from IME. + */ + private void clearLowProfileModeIfNeeded(@InsetsType int showTypes, boolean fromIme) { + final SystemUiVisibilityInfo info = mCompatibleVisibilityInfo; + if ((showTypes & Type.systemBars()) != 0 && !fromIme + && (info.globalVisibility & SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { + info.globalVisibility &= ~SYSTEM_UI_FLAG_LOW_PROFILE; + info.localChanges |= SYSTEM_UI_FLAG_LOW_PROFILE; + dispatchDispatchSystemUiVisibilityChanged(info); + } + } + + private void dispatchDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) { + if (mDispatchedSystemUiVisibility != args.globalVisibility) { mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY); - mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, info)); + mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); } } @@ -2073,6 +2092,7 @@ public final class ViewRootImpl implements ViewParent, final int sysUiVis = inOutParams.systemUiVisibility | inOutParams.subtreeSystemUiVisibility; final int flags = inOutParams.flags; final int type = inOutParams.type; + final int adjust = inOutParams.softInputMode & SOFT_INPUT_MASK_ADJUST; if ((inOutParams.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { inOutParams.insetsFlags.appearance = 0; @@ -2098,12 +2118,13 @@ public final class ViewRootImpl implements ViewParent, } } + inOutParams.privateFlags &= ~PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + if ((inOutParams.privateFlags & PRIVATE_FLAG_FIT_INSETS_CONTROLLED) != 0) { return; } int types = inOutParams.getFitInsetsTypes(); - int sides = inOutParams.getFitInsetsSides(); boolean ignoreVis = inOutParams.isFitInsetsIgnoringVisibility(); if (((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0 @@ -2118,10 +2139,13 @@ public final class ViewRootImpl implements ViewParent, if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { ignoreVis = true; } else if ((types & Type.systemBars()) == Type.systemBars()) { - types |= Type.ime(); + if (adjust == SOFT_INPUT_ADJUST_RESIZE) { + types |= Type.ime(); + } else { + inOutParams.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + } } inOutParams.setFitInsetsTypes(types); - inOutParams.setFitInsetsSides(sides); inOutParams.setFitInsetsIgnoringVisibility(ignoreVis); // The fitting of insets are not really controlled by the clients, so we remove the flag. @@ -2491,8 +2515,7 @@ public final class ViewRootImpl implements ViewParent, if (mFirst || mAttachInfo.mViewVisibilityChanged) { mAttachInfo.mViewVisibilityChanged = false; - int resizeMode = mSoftInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; + int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST; // If we are in auto resize mode, then we need to determine // what mode to use now. if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { @@ -2505,11 +2528,8 @@ public final class ViewRootImpl implements ViewParent, if (resizeMode == 0) { resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; } - if ((lp.softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) { - lp.softInputMode = (lp.softInputMode & - ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | - resizeMode; + if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) { + lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode; params = lp; } } @@ -5005,6 +5025,7 @@ public final class ViewRootImpl implements ViewParent, String.format("Calling showInsets(%d,%b) on window that no longer" + " has views.", msg.arg1, msg.arg2 == 1)); } + clearLowProfileModeIfNeeded(msg.arg1, msg.arg2 == 1); mInsetsController.show(msg.arg1, msg.arg2 == 1); break; } diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 1a9003581078..24538c5205b0 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -33,7 +33,6 @@ import java.lang.annotation.RetentionPolicy; /** * Interface to control windows that generate insets. * - * TODO(118118435): Needs more information and examples once the API is more baked. */ public interface WindowInsetsController { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5c6269421a1f..2a711f6974f3 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -2033,6 +2033,12 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 0x10000000; /** + * Flag to indicate that the parent frame of a window should be inset by IME. + * @hide + */ + public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000; + + /** * An internal annotation for flags that can be specified to {@link #softInputMode}. * * @hide diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0d21673dfc71..5f8b13b5b548 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -611,7 +611,8 @@ public final class InputMethodManager { @Override public void startInputAsyncOnWindowFocusGain(View focusedView, @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) { - final int startInputFlags = getStartInputFlags(focusedView, 0); + int startInputFlags = getStartInputFlags(focusedView, 0); + startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS; final ImeFocusController controller = getFocusController(); if (controller == null) { diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 970d70cf1fb4..f56c357c57a8 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -52,6 +52,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -306,9 +307,6 @@ public class ProgressBar extends View { setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); - // onProgressRefresh() is only called when the progress changes. So we should set - // stateDescription during initialization here. - super.setStateDescription(formatStateDescription(mProgress)); setSecondaryProgress(a.getInt( R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); @@ -1601,7 +1599,8 @@ public class ProgressBar extends View { } void onProgressRefresh(float scale, boolean fromUser, int progress) { - if (mCustomStateDescription == null) { + if (AccessibilityManager.getInstance(mContext).isEnabled() + && mCustomStateDescription == null) { super.setStateDescription(formatStateDescription(mProgress)); } } @@ -2325,6 +2324,7 @@ public class ProgressBar extends View { AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(), getProgress()); info.setRangeInfo(rangeInfo); + info.setStateDescription(formatStateDescription(mProgress)); } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index fe774780c133..76c2f2e4c60b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -273,8 +273,6 @@ public class ChooserActivity extends ResolverActivity implements private int mLastNumberOfChildren = -1; private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment"; - // TODO: Update to handle landscape instead of using static value - private static final int MAX_RANKED_TARGETS = 4; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); private final Set<Pair<ComponentName, UserHandle>> mServicesRequested = new HashSet<>(); @@ -951,7 +949,7 @@ public class ChooserActivity extends ResolverActivity implements updateStickyContentPreview(); if (shouldShowStickyContentPreview() || mChooserMultiProfilePagerAdapter - .getCurrentRootAdapter().getContentPreviewRowCount() != 0) { + .getCurrentRootAdapter().getSystemRowCount() != 0) { logActionShareWithPreview(); } return postRebuildListInternal(rebuildCompleted); @@ -1316,13 +1314,14 @@ public class ChooserActivity extends ResolverActivity implements ViewGroup parent) { ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( R.layout.chooser_grid_preview_image, parent, false); + ViewGroup imagePreview = contentPreviewLayout.findViewById(R.id.content_preview_image_area); final ViewGroup actionRow = (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row); //TODO: addActionButton(actionRow, createCopyButton()); addActionButton(actionRow, createNearbyButton(targetIntent)); - mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, true); + mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { @@ -1342,7 +1341,7 @@ public class ChooserActivity extends ResolverActivity implements if (imageUris.size() == 0) { Log.i(TAG, "Attempted to display image preview area with zero" + " available images detected in EXTRA_STREAM list"); - contentPreviewLayout.setVisibility(View.GONE); + imagePreview.setVisibility(View.GONE); return contentPreviewLayout; } @@ -2683,7 +2682,7 @@ public class ChooserActivity extends ResolverActivity implements final int bottomInset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0; int offset = bottomInset; - int rowsToShow = gridAdapter.getContentPreviewRowCount() + int rowsToShow = gridAdapter.getSystemRowCount() + gridAdapter.getProfileRowCount() + gridAdapter.getServiceTargetRowCount() + gridAdapter.getCallerAndRankedTargetRowCount(); @@ -3283,7 +3282,7 @@ public class ChooserActivity extends ResolverActivity implements public int getRowCount() { return (int) ( - getContentPreviewRowCount() + getSystemRowCount() + getProfileRowCount() + getServiceTargetRowCount() + getCallerAndRankedTargetRowCount() @@ -3295,22 +3294,21 @@ public class ChooserActivity extends ResolverActivity implements } /** - * Returns either {@code 0} or {@code 1} depending on whether we want to show the list item - * content preview. Not to be confused with the sticky content preview which is above the - * personal and work tabs. + * Whether the "system" row of targets is displayed. + * This area includes the content preview (if present) and action row. */ - public int getContentPreviewRowCount() { + public int getSystemRowCount() { // For the tabbed case we show the sticky content preview above the tabs, // please refer to shouldShowStickyContentPreview if (shouldShowTabs()) { return 0; } + if (!isSendAction(getTargetIntent())) { return 0; } - if (mHideContentPreview || mChooserListAdapter == null - || mChooserListAdapter.getCount() == 0) { + if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) { return 0; } @@ -3352,7 +3350,7 @@ public class ChooserActivity extends ResolverActivity implements @Override public int getItemCount() { return (int) ( - getContentPreviewRowCount() + getSystemRowCount() + getProfileRowCount() + getServiceTargetRowCount() + getCallerAndRankedTargetRowCount() @@ -3407,7 +3405,7 @@ public class ChooserActivity extends ResolverActivity implements public int getItemViewType(int position) { int count; - int countSum = (count = getContentPreviewRowCount()); + int countSum = (count = getSystemRowCount()); if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW; countSum += (count = getProfileRowCount()); @@ -3631,7 +3629,7 @@ public class ChooserActivity extends ResolverActivity implements } int getListPosition(int position) { - position -= getContentPreviewRowCount() + getProfileRowCount(); + position -= getSystemRowCount() + getProfileRowCount(); final int serviceCount = mChooserListAdapter.getServiceTargetCount(); final int serviceRows = (int) Math.ceil((float) serviceCount diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index eb59f0f59be1..b4cd145ca374 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -390,6 +390,20 @@ public final class SystemUiDeviceConfigFlags { public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled"; /** + * (float) Weight bonus applied on top sharing shortcuts as per native ranking provided by apps. + * Its range need to be 0 ~ 1. + */ + public static final String TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER = + "top_native_ranked_sharing_shortcut_booster"; + + /** + * (float) Weight bonus applied on 2nd top sharing shortcuts as per native ranking provided by + * apps. Its range need to be 0 ~ 1. + */ + public static final String NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER = + "non_top_native_ranked_sharing_shortcut_booster"; + + /** * (boolean) Whether to enable user-drag resizing for PIP. */ public static final String PIP_USER_RESIZE = "pip_user_resize"; @@ -409,6 +423,11 @@ public final class SystemUiDeviceConfigFlags { */ public static final String BACK_GESTURE_SLOP_MULTIPLIER = "back_gesture_slop_multiplier"; + /** + * (long) Screenshot keychord delay (how long the buttons must be pressed), in ms + */ + public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 37f68233db53..3bcba75ec163 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -224,6 +224,8 @@ public final class InputMethodDebug { return "HIDE_DOCKED_STACK_ATTACHED"; case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION: return "HIDE_RECENTS_ANIMATION"; + case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR: + return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR"; default: return "Unknown=" + reason; } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index 4b968b45f122..f46626be48a8 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -47,7 +47,8 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED, SoftInputShowHideReason.HIDE_RECENTS_ANIMATION, - SoftInputShowHideReason.HIDE_BUBBLES}) + SoftInputShowHideReason.HIDE_BUBBLES, + SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR}) public @interface SoftInputShowHideReason { /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ int SHOW_SOFT_INPUT = 0; @@ -147,4 +148,17 @@ public @interface SoftInputShowHideReason { * switching, or collapsing Bubbles. */ int HIDE_BUBBLES = 19; + + /** + * Hide soft input when focusing the same window (e.g. screen turned-off and turn-on) which no + * valid focused editor. + * + * Note: From Android R, the window focus change callback is processed by InputDispatcher, + * some focus behavior changes (e.g. There are an activity with a dialog window, after + * screen turned-off and turned-on, before Android R the window focus sequence would be + * the activity first and then the dialog focused, however, in R the focus sequence would be + * only the dialog focused as it's the latest window with input focus) makes we need to hide + * soft-input when the same window focused again to align with the same behavior prior to R. + */ + int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20; } diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java index 5a8d2c227256..ac83987ef12c 100644 --- a/core/java/com/android/internal/inputmethod/StartInputFlags.java +++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java @@ -47,4 +47,10 @@ public @interface StartInputFlags { * documented hence we probably need to revisit this though. */ int INITIAL_CONNECTION = 4; + + /** + * The start input happens when the window gained focus to call + * {@code android.view.inputmethod.InputMethodManager#startInputAsyncOnWindowFocusGain}. + */ + int WINDOW_GAINED_FOCUS = 8; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 5a1af84eccac..2ac7e5f28e52 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -151,7 +151,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -13596,6 +13596,7 @@ public class BatteryStatsImpl extends BatteryStats { mDailyStartTime = in.readLong(); mNextMinDailyDeadline = in.readLong(); mNextMaxDailyDeadline = in.readLong(); + mBatteryTimeToFullSeconds = in.readLong(); mStartCount++; @@ -14086,6 +14087,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mDailyStartTime); out.writeLong(mNextMinDailyDeadline); out.writeLong(mNextMaxDailyDeadline); + out.writeLong(mBatteryTimeToFullSeconds); mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); @@ -14669,6 +14671,7 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in); mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in); mLastWriteTime = in.readLong(); + mBatteryTimeToFullSeconds = in.readLong(); mRpmStats.clear(); int NRPMS = in.readInt(); @@ -14861,6 +14864,7 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeLightDozeCounter.writeToParcel(out); mDischargeDeepDozeCounter.writeToParcel(out); out.writeLong(mLastWriteTime); + out.writeLong(mBatteryTimeToFullSeconds); out.writeInt(mRpmStats.size()); for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index b12c5e9ba5b0..fbbf7916b31e 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1094,13 +1094,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mLastWindowFlags = attrs.flags; if (insets != null) { - final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars()); final Insets stableBarInsets = insets.getInsetsIgnoringVisibility( WindowInsets.Type.systemBars()); - mLastTopInset = systemBarInsets.top; - mLastBottomInset = systemBarInsets.bottom; - mLastRightInset = systemBarInsets.right; - mLastLeftInset = systemBarInsets.left; + final Insets systemInsets = Insets.min( + insets.getInsets(WindowInsets.Type.systemBars() + | WindowInsets.Type.displayCutout()), stableBarInsets); + mLastTopInset = systemInsets.top; + mLastBottomInset = systemInsets.bottom; + mLastRightInset = systemInsets.right; + mLastLeftInset = systemInsets.left; // Don't animate if the presence of stable insets has changed, because that // indicates that the window was either just added and received them for the diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index e3a456cc94a4..0791ed3c42ec 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -167,6 +167,8 @@ public class ConversationLayout extends FrameLayout private int mFacePileProtectionWidthExpanded; private boolean mImportantConversation; private TextView mUnreadBadge; + private ViewGroup mAppOps; + private Rect mAppOpsTouchRect = new Rect(); private float mMinTouchSize; private Icon mConversationIcon; private Icon mShortcutIcon; @@ -208,6 +210,7 @@ public class ConversationLayout extends FrameLayout mConversationIconView = findViewById(R.id.conversation_icon); mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); + mAppOps = findViewById(com.android.internal.R.id.app_ops); mMinTouchSize = 48 * getResources().getDisplayMetrics().density; mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring); mConversationIconBadge = findViewById(R.id.conversation_icon_badge); @@ -1163,6 +1166,47 @@ public class ConversationLayout extends FrameLayout } }); } + if (mAppOps.getWidth() > 0) { + + // Let's increase the touch size of the app ops view if it's here + mAppOpsTouchRect.set( + mAppOps.getLeft(), + mAppOps.getTop(), + mAppOps.getRight(), + mAppOps.getBottom()); + for (int i = 0; i < mAppOps.getChildCount(); i++) { + View child = mAppOps.getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + // Make sure each child has at least a minTouchSize touch target around it + float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f + - mMinTouchSize / 2.0f; + float childTouchRight = childTouchLeft + mMinTouchSize; + mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left, + mAppOps.getLeft() + childTouchLeft); + mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right, + mAppOps.getLeft() + childTouchRight); + } + + // Increase the height + int heightIncrease = 0; + if (mAppOpsTouchRect.height() < mMinTouchSize) { + heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height()) + / 2.0f); + } + mAppOpsTouchRect.inset(0, -heightIncrease); + + // Let's adjust the hitrect since app ops isn't a direct child + ViewGroup viewGroup = (ViewGroup) mAppOps.getParent(); + while (viewGroup != this) { + mAppOpsTouchRect.offset(viewGroup.getLeft(), viewGroup.getTop()); + viewGroup = (ViewGroup) viewGroup.getParent(); + } + // + // Extend the size of the app opps to be at least 48dp + setTouchDelegate(new TouchDelegate(mAppOpsTouchRect, mAppOps)); + } } public MessagingLinearLayout getMessagingLinearLayout() { diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 5c045b65be22..7a5c38385f32 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -20,6 +20,7 @@ #include "android_media_AudioTrack.h" #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include "core_jni_helpers.h" #include <utils/Log.h> @@ -251,7 +252,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, jlong nativeAudioTrack, jboolean offload, jint encapsulationMode, - jobject tunerConfiguration) { + jobject tunerConfiguration, jstring opPackageName) { ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d," " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p", jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, @@ -337,7 +338,8 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we } // create the native AudioTrack object - lpTrack = new AudioTrack(); + ScopedUtfChars opPackageNameStr(env, opPackageName); + lpTrack = new AudioTrack(opPackageNameStr.c_str()); // read the AudioAttributes values auto paa = JNIAudioAttributeHelper::makeUnique(); @@ -371,23 +373,24 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we status_t status = NO_ERROR; switch (memoryMode) { case MODE_STREAM: - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - offload ? 0 : frameCount, - offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - 0,// shared mem - true,// thread can call Java - sessionId,// audio session ID - offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC, - offload ? &offloadInfo : NULL, - -1, -1, // default uid, pid values - paa.get()); - + status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed + // in paa (last argument) + sampleRateInHertz, + format, // word length, PCM + nativeChannelMask, offload ? 0 : frameCount, + offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD + : AUDIO_OUTPUT_FLAG_NONE, + audioCallback, + &(lpJniStorage->mCallbackData), // callback, callback data (user) + 0, // notificationFrames == 0 since not using EVENT_MORE_DATA + // to feed the AudioTrack + 0, // shared mem + true, // thread can call Java + sessionId, // audio session ID + offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK + : AudioTrack::TRANSFER_SYNC, + offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values + paa.get()); break; case MODE_STATIC: @@ -398,22 +401,22 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we goto native_init_failure; } - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - frameCount, - AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - lpJniStorage->mMemBase,// shared mem - true,// thread can call Java - sessionId,// audio session ID - AudioTrack::TRANSFER_SHARED, - NULL, // default offloadInfo - -1, -1, // default uid, pid values - paa.get()); + status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed + // in paa (last argument) + sampleRateInHertz, + format, // word length, PCM + nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, + audioCallback, + &(lpJniStorage->mCallbackData), // callback, callback data (user) + 0, // notificationFrames == 0 since not using EVENT_MORE_DATA + // to feed the AudioTrack + lpJniStorage->mMemBase, // shared mem + true, // thread can call Java + sessionId, // audio session ID + AudioTrack::TRANSFER_SHARED, + NULL, // default offloadInfo + -1, -1, // default uid, pid values + paa.get()); break; default: @@ -1428,7 +1431,8 @@ static const JNINativeMethod gMethods[] = { {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I", + {"native_setup", + "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I", (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index acf8cc4944d9..cfd428cbacc3 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -119,6 +119,14 @@ message SecureSettingsProto { } optional Assist assist = 7; + message AssistHandles { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto learning_time_elapsed_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto learning_event_count = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional AssistHandles assist_handles = 86; + message Autofill { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -596,5 +604,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 85; + // Next tag = 87; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d0ca0a86efb0..6a92a83c9899 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5036,6 +5036,10 @@ <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" android:protectionLevel="signature|appPredictor" /> + <!-- @hide Allows an application to create/destroy input consumer. --> + <permission android:name="android.permission.INPUT_CONSUMER" + android:protectionLevel="signature" /> + <!-- Attribution for Country Detector. --> <attribution android:tag="CountryDetector" android:label="@string/country_detector"/> <!-- Attribution for Location service. --> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 03e130e8023b..23b8bd34829e 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -146,6 +146,43 @@ android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" /> + <LinearLayout + android:id="@+id/app_ops" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_marginStart="6dp" + android:background="?android:selectableItemBackgroundBorderless" + android:orientation="horizontal"> + <ImageView + android:id="@+id/camera" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_camera" + android:visibility="gone" + android:focusable="false" + android:contentDescription="@string/notification_appops_camera_active" + /> + <ImageView + android:id="@+id/mic" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_mic" + android:layout_marginStart="4dp" + android:visibility="gone" + android:focusable="false" + android:contentDescription="@string/notification_appops_microphone_active" + /> + <ImageView + android:id="@+id/overlay" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_alert_window_layer" + android:layout_marginStart="4dp" + android:visibility="gone" + android:focusable="false" + android:contentDescription="@string/notification_appops_overlay_active" + /> + </LinearLayout> <include layout="@layout/notification_material_media_transfer_action" android:id="@+id/media_seamless" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index ea8498ab33da..57f2d6aea741 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1216,7 +1216,7 @@ <string name="dump_heap_ready_text" msgid="5849618132123045516">"የ<xliff:g id="PROC">%1$s</xliff:g> ሂደት ተራጋፊ ክምር ለማጋራት ለእርስዎ ይገኛል። ይጠንቀቁ፦ ይህ ተራጋፊ ክምር ሂደቱ ሊደርስባቸው የሚችለው ማንኛውም የግል መረጃ ሊኖረው ይችላል፣ ይህ እርስዎ የተየቧቸውን ነገሮች ሊያካትት ይችላል።"</string> <string name="sendText" msgid="493003724401350724">"ለፅሁፍ ድርጊት ምረጥ"</string> <string name="volume_ringtone" msgid="134784084629229029">"የስልክ ጥሪ ድምፅ"</string> - <string name="volume_music" msgid="7727274216734955095">" ማህደረ መረጃ ክፍልፍል"</string> + <string name="volume_music" msgid="7727274216734955095">"የማህደረ መረጃ ድምጽ መጠን"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"በብሉቱዝ በኩል ማጫወት"</string> <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"የፀጥታ የስልክ የደውል ድምፅ ተዘጋጅቷል"</string> <string name="volume_call" msgid="7625321655265747433">"የጥሪ ላይ ድም ፅ መጨመሪያ/መቀነሻ"</string> @@ -1227,7 +1227,7 @@ <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"የብሉቱዝ ድምፅ መጠን"</string> <string name="volume_icon_description_ringer" msgid="2187800636867423459">"የስልክ ጥሪ ድምፅ መጠን"</string> <string name="volume_icon_description_incall" msgid="4491255105381227919">"የስልክ ጥሪ ድምፅ መጠን"</string> - <string name="volume_icon_description_media" msgid="4997633254078171233">"የማህደረ መረጃ ክፍልፍል"</string> + <string name="volume_icon_description_media" msgid="4997633254078171233">"የማህደረ መረጃ ድምጽ መጠን"</string> <string name="volume_icon_description_notification" msgid="579091344110747279">"የማሳወቂያ ክፍልፍል"</string> <string name="ringtone_default" msgid="9118299121288174597">"ነባሪ የስልክ ላይ ጥሪ"</string> <string name="ringtone_default_with_actual" msgid="2709686194556159773">"ነባሪ (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 6978a9aefb17..4a99ec8021e8 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -270,9 +270,9 @@ <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"وضع صامت"</string> <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"الصوت متوقف"</string> <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"الصوت قيد التفعيل"</string> - <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطائرة"</string> - <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطائرة قيد التفعيل"</string> - <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطائرة متوقف"</string> + <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطيران"</string> + <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطيران قيد التفعيل"</string> + <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطيران متوقف"</string> <string name="global_action_settings" msgid="4671878836947494217">"الإعدادات"</string> <string name="global_action_assist" msgid="2517047220311505805">"مساعدة"</string> <string name="global_action_voice_assist" msgid="6655788068555086695">"المساعد الصوتي"</string> @@ -398,7 +398,7 @@ <string name="permlab_getPackageSize" msgid="375391550792886641">"قياس مساحة تخزين التطبيق"</string> <string name="permdesc_getPackageSize" msgid="742743530909966782">"للسماح للتطبيق باسترداد شفرته وبياناته وأحجام ذاكرات التخزين المؤقت"</string> <string name="permlab_writeSettings" msgid="8057285063719277394">"تعديل إعدادات النظام"</string> - <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة تهيئة نظامك."</string> + <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة إعداد نظامك."</string> <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"العمل عند بدء التشغيل"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"للسماح للتطبيق ببدء تشغيل نفسه عقب انتهاء النظام من التشغيل. قد يؤدي ذلك إلى استغراق المزيد من الوقت عند بدء الجهاز اللوحي والسماح للتطبيق بإبطاء الأداء الإجمالي للجهاز اللوحي من خلال تشغيله دائمًا."</string> <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"للسماح بتشغيل التطبيق تلقائيًا بعد الانتهاء من بدء تشغيل النظام. وقد يؤدي ذلك إلى إطالة فترة بدء تشغيل جهاز Android TV، بالإضافة إلى أنه يسمح للتطبيق بإبطاء أداء الجهاز بشكل عام لأنه يتم تشغيله بشكل دائم."</string> @@ -507,7 +507,7 @@ <string name="permlab_accessWifiState" msgid="5552488500317911052">"عرض اتصالات Wi-Fi"</string> <string name="permdesc_accessWifiState" msgid="6913641669259483363">"للسماح للتطبيق بعرض معلومات حول شبكات Wi-Fi، كعرض معلومات حول ما إذا تم تفعيل Wi-Fi واسم أجهزة Wi-Fi المتصلة."</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"التوصيل والفصل من Wi-Fi"</string> - <string name="permdesc_changeWifiState" msgid="7170350070554505384">"للسماح للتطبيق بالاتصال بنقاط الوصول إلى Wi-Fi وقطع الاتصال بها، وإجراء تغييرات على تهيئة الجهاز لشبكات Wi-Fi."</string> + <string name="permdesc_changeWifiState" msgid="7170350070554505384">"للسماح للتطبيق بالاتصال بنقاط الوصول إلى Wi-Fi وقطع الاتصال بها، وإجراء تغييرات على إعداد الجهاز لشبكات Wi-Fi."</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"السماح باستقبال بث Wi-Fi متعدد"</string> <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"للسماح للتطبيق بتلقي الحزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهازك اللوحي فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق وضع البث غير المتعدد."</string> <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"للسماح للتطبيق بتلقّي الحِزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهاز Android TV فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق ما يتم استهلاكه في وضع البث غير المتعدد."</string> @@ -523,9 +523,9 @@ <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"للسماح للتطبيق بتوصيل جهاز Android TV بشبكات WiMAX وقطع اتصاله بها."</string> <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"للسماح للتطبيق بتوصيل الهاتف بشبكات WiMAX وقطع اتصاله بها."</string> <string name="permlab_bluetooth" msgid="586333280736937209">"الاتصال بأجهزة بلوتوث"</string> - <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"للسماح للتطبيق بعرض تهيئة البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> + <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"للسماح للتطبيق بعرض إعداد البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"للسماح للتطبيق بعرض بيانات ضبط البلوتوث على جهاز Android TV وإجراء اتصالات مع الأجهزة المقترنة وقبولها."</string> - <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"للسماح للتطبيق بعرض تهيئة البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> + <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"للسماح للتطبيق بعرض إعداد البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"يسمح هذا الإذن للتطبيق بالحصول على معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل، مثلاً المساعدات المسجّلة ووجهة المسار."</string> <string name="permlab_nfc" msgid="1904455246837674977">"التحكم في اتصال الحقل القريب"</string> @@ -661,8 +661,8 @@ <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"للسماح للمالك بالربط بواجهة المستوى العلوي لخدمة موفر الحالة. لن تكون هناك حاجة إلى هذا الإعداد مطلقًا مع التطبيقات العادية."</string> <string name="permlab_bindDreamService" msgid="4776175992848982706">"الالتزام بخدمة dream"</string> <string name="permdesc_bindDreamService" msgid="9129615743300572973">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة dream. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> - <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال"</string> - <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> + <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال"</string> + <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"الاستماع إلى ملاحظات حول أحوال الشبكة"</string> <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"للسماح للتطبيق بالاستماع إلى ملاحظات حول أحوال الشبكة. لا حاجة إلى هذا مع التطبيقات العادية."</string> <string name="permlab_setInputCalibration" msgid="932069700285223434">"تغيير معايرة أجهزة الإدخال"</string> @@ -678,7 +678,7 @@ <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"الالتزام بخدمات مشغل شبكة الجوال"</string> <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"للسماح للمالك بالالتزام بخدمات مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_access_notification_policy" msgid="5524112842876975537">"الوصول إلى إعداد \"عدم الإزعاج\""</string> - <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة تهيئة \"عدم الإزعاج\" وكتابتها."</string> + <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string> @@ -1298,7 +1298,7 @@ <string name="volume_ringtone" msgid="134784084629229029">"مستوى صوت الرنين"</string> <string name="volume_music" msgid="7727274216734955095">"مستوى صوت الوسائط"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"تشغيل من خلال البلوتوث"</string> - <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"تم تعيين نغمة الرنين الصامتة"</string> + <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"تم ضبط نغمة الرنين الصامتة"</string> <string name="volume_call" msgid="7625321655265747433">"مستوى صوت المكالمات الواردة"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"مستوى صوت المكالمة الواردة بالبلوتوث"</string> <string name="volume_alarm" msgid="4486241060751798448">"مستوى صوت المنبّه"</string> @@ -1462,7 +1462,7 @@ <string name="ext_media_status_unmountable" msgid="7043574843541087748">"تالف"</string> <string name="ext_media_status_unsupported" msgid="5460509911660539317">"غير متوافق"</string> <string name="ext_media_status_ejecting" msgid="7532403368044013797">"جارٍ إنهاء التحميل…"</string> - <string name="ext_media_status_formatting" msgid="774148701503179906">"جارٍ التهيئة…"</string> + <string name="ext_media_status_formatting" msgid="774148701503179906">"تجري التهيئة..."</string> <string name="ext_media_status_missing" msgid="6520746443048867314">"لم يتم الإدخال"</string> <string name="activity_list_empty" msgid="4219430010716034252">"لم يتم العثور على أي أنشطة متطابقة."</string> <string name="permlab_route_media_output" msgid="8048124531439513118">"توجيه إخراج الوسائط"</string> @@ -2155,7 +2155,7 @@ <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"جدول بيانات: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string> <string name="mime_type_presentation" msgid="1145384236788242075">"عرض تقديمي"</string> <string name="mime_type_presentation_ext" msgid="8761049335564371468">"عرض تقديمي: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string> - <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطائرة\"."</string> + <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطيران\"."</string> <string name="car_loading_profile" msgid="8219978381196748070">"جارٍ التحميل"</string> <plurals name="file_count" formatted="false" msgid="7063513834724389247"> <item quantity="zero"><xliff:g id="FILE_NAME_2">%s</xliff:g> و<xliff:g id="COUNT_3">%d</xliff:g> ملف</item> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 18b69875b901..6fa8b88b09f5 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -241,7 +241,7 @@ <string name="global_action_power_off" msgid="4404936470711393203">"Isključi"</string> <string name="global_action_power_options" msgid="1185286119330160073">"Napajanje"</string> <string name="global_action_restart" msgid="4678451019561687074">"Restartuj"</string> - <string name="global_action_emergency" msgid="1387617624177105088">"Hitni poziv"</string> + <string name="global_action_emergency" msgid="1387617624177105088">"Hitan poziv"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"Izveštaj o grešci"</string> <string name="global_action_logout" msgid="6093581310002476511">"Završi sesiju"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"Snimak ekrana"</string> @@ -832,7 +832,7 @@ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string> - <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitni poziv"</string> + <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitan poziv"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string> @@ -1815,7 +1815,7 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string> <string name="battery_saver_description" msgid="6794188153647295212">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“"</string> <string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 74f0e68f5f3f..df7fca0bc6e1 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -717,10 +717,10 @@ <item msgid="6216981255272016212">"Асаблівы"</item> </string-array> <string-array name="emailAddressTypes"> - <item msgid="7786349763648997741">"Хатні"</item> - <item msgid="435564470865989199">"Працоўны"</item> - <item msgid="4199433197875490373">"Іншы"</item> - <item msgid="3233938986670468328">"Карыстальніцкі"</item> + <item msgid="7786349763648997741">"Асабістая"</item> + <item msgid="435564470865989199">"Працоўная"</item> + <item msgid="4199433197875490373">"Іншая"</item> + <item msgid="3233938986670468328">"Карыстальніцкая"</item> </string-array> <string-array name="postalAddressTypes"> <item msgid="3861463339764243038">"На Галоўную старонку"</item> @@ -770,14 +770,14 @@ <string name="phoneTypeWorkPager" msgid="3748332310638505234">"Працоўны пэйджар"</string> <string name="phoneTypeAssistant" msgid="757550783842231039">"Асістэнт"</string> <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string> - <string name="eventTypeCustom" msgid="3257367158986466481">"Карыстальніцкі"</string> + <string name="eventTypeCustom" msgid="3257367158986466481">"Карыстальніцкае"</string> <string name="eventTypeBirthday" msgid="7770026752793912283">"Дзень нараджэння"</string> <string name="eventTypeAnniversary" msgid="4684702412407916888">"Гадавіна"</string> <string name="eventTypeOther" msgid="530671238533887997">"Іншае"</string> - <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкі"</string> + <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкая"</string> <string name="emailTypeHome" msgid="1597116303154775999">"Хатні"</string> - <string name="emailTypeWork" msgid="2020095414401882111">"Працоўны"</string> - <string name="emailTypeOther" msgid="5131130857030897465">"Іншы"</string> + <string name="emailTypeWork" msgid="2020095414401882111">"Працоўная"</string> + <string name="emailTypeOther" msgid="5131130857030897465">"Іншая"</string> <string name="emailTypeMobile" msgid="787155077375364230">"Мабільны"</string> <string name="postalTypeCustom" msgid="5645590470242939129">"Карыстальніцкі"</string> <string name="postalTypeHome" msgid="7562272480949727912">"Хатні"</string> @@ -800,19 +800,19 @@ <string name="orgTypeWork" msgid="8684458700669564172">"Працоўная"</string> <string name="orgTypeOther" msgid="5450675258408005553">"Іншая"</string> <string name="orgTypeCustom" msgid="1126322047677329218">"Карыстальніцкі"</string> - <string name="relationTypeCustom" msgid="282938315217441351">"Карыстальніцкі"</string> + <string name="relationTypeCustom" msgid="282938315217441351">"Карыстальніцкае"</string> <string name="relationTypeAssistant" msgid="4057605157116589315">"Памочнік"</string> <string name="relationTypeBrother" msgid="7141662427379247820">"Брат"</string> <string name="relationTypeChild" msgid="9076258911292693601">"Дзіця"</string> - <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Унутраны Партнёр"</string> + <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Сужыцель/сужыцелька"</string> <string name="relationTypeFather" msgid="3856225062864790596">"Бацька"</string> <string name="relationTypeFriend" msgid="3192092625893980574">"Сябар/сяброўка"</string> <string name="relationTypeManager" msgid="2272860813153171857">"Кіраўнік"</string> <string name="relationTypeMother" msgid="2331762740982699460">"Маці"</string> - <string name="relationTypeParent" msgid="4177920938333039882">"Бацька"</string> + <string name="relationTypeParent" msgid="4177920938333039882">"Бацька/маці"</string> <string name="relationTypePartner" msgid="4018017075116766194">"Партнёр"</string> - <string name="relationTypeReferredBy" msgid="5285082289602849400">"Запрошаны"</string> - <string name="relationTypeRelative" msgid="3396498519818009134">"Адносны"</string> + <string name="relationTypeReferredBy" msgid="5285082289602849400">"Рэкамендацыя"</string> + <string name="relationTypeRelative" msgid="3396498519818009134">"Радня"</string> <string name="relationTypeSister" msgid="3721676005094140671">"Сястра"</string> <string name="relationTypeSpouse" msgid="6916682664436031703">"Муж/жонка"</string> <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Карыстальніцкі"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 02dd4f2bfe3c..5c5f82208499 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -590,7 +590,7 @@ <string name="face_acquired_not_detected" msgid="2945945257956443257">"Postavite lice direktno ispred telefona"</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Previše pokreta. Držite telefon mirno."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Ponovo registrirajte lice."</string> - <string name="face_acquired_too_different" msgid="4699657338753282542">"Nije više moguće prepoznati lice. Pokušajte opet."</string> + <string name="face_acquired_too_different" msgid="4699657338753282542">"Više nije moguće prepoznati lice. Pokušajte opet."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Previše slično, promijenite položaj."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Malo manje zakrenite glavu."</string> <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Malo manje zakrenite glavu."</string> @@ -970,7 +970,7 @@ <string name="save_password_remember" msgid="6490888932657708341">"Zapamti"</string> <string name="save_password_never" msgid="6776808375903410659">"Nikad"</string> <string name="open_permission_deny" msgid="5136793905306987251">"Nemate odobrenje za otvaranje ove stranice."</string> - <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međuspremnik."</string> + <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međumemoriju."</string> <string name="copied" msgid="4675902854553014676">"Kopirano"</string> <string name="more_item_label" msgid="7419249600215749115">"Više"</string> <string name="prepend_shortcut_label" msgid="1743716737502867951">"Meni+"</string> @@ -1280,7 +1280,7 @@ <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string> <string name="accept" msgid="5447154347815825107">"Prihvati"</string> <string name="decline" msgid="6490507610282145874">"Odbijte"</string> - <string name="select_character" msgid="3352797107930786979">"Umetni karakter"</string> + <string name="select_character" msgid="3352797107930786979">"Umetni znak"</string> <string name="sms_control_title" msgid="4748684259903148341">"Slanje SMS poruka"</string> <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> šalje veliki broj SMS poruka. Da li želite dozvoliti ovoj aplikaciji da nastavi slanje poruka?"</string> <string name="sms_control_yes" msgid="4858845109269524622">"Dozvoli"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 6529de8af786..09b527904967 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -988,8 +988,8 @@ <string name="searchview_description_submit" msgid="6771060386117334686">"Envia la consulta"</string> <string name="searchview_description_voice" msgid="42360159504884679">"Cerca per veu"</string> <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Vols activar l\'exploració tàctil?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb la tauleta."</string> - <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb el telèfon."</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb la tauleta."</string> + <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb el telèfon."</string> <string name="oneMonthDurationPast" msgid="4538030857114635777">"Fa 1 mes"</string> <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Fa més d\'1 mes"</string> <plurals name="last_num_days" formatted="false" msgid="687443109145393632"> @@ -1221,7 +1221,7 @@ <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"S\'ha establert el so de silenci"</string> <string name="volume_call" msgid="7625321655265747433">"Volum en trucada"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"Volum en trucada per Bluetooth"</string> - <string name="volume_alarm" msgid="4486241060751798448">"Volum de l\'alarma"</string> + <string name="volume_alarm" msgid="4486241060751798448">"Volum d\'alarma"</string> <string name="volume_notification" msgid="6864412249031660057">"Volum de notificacions"</string> <string name="volume_unknown" msgid="4041914008166576293">"Volum"</string> <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Volum del Bluetooth"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 654619cb668f..587562f232c4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1792,8 +1792,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string> - <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string> + <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string> <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string> @@ -1999,9 +1999,9 @@ <string name="notification_appops_overlay_active" msgid="5571732753262836481">"wird über anderen Apps auf dem Bildschirm angezeigt"</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index b8c00d049721..1024729b53fe 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -421,7 +421,7 @@ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Esta app puede agregar, quitar o cambiar eventos del calendario en tu teléfono. Puede enviar mensajes que parecen proceder de propietarios del calendario o cambiar eventos sin notificarlos."</string> <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"acceder a comandos adicionales del proveedor del lugar"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string> - <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación exacta solo en primer plano"</string> + <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación precisa solo en primer plano"</string> <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Mientras la usas, esta app puede obtener tu ubicación exacta mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados. Es posible que esto aumente el uso de batería."</string> <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"acceder a la ubicación aproximada solo en primer plano"</string> <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Mientras la usas, esta app puede obtener tu ubicación aproximada mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 3ab793e81771..bbdfc62dbcc1 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1264,7 +1264,7 @@ <string name="sms_control_title" msgid="4748684259903148341">"درحال ارسال پیامکها"</string> <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> درحال ارسال تعداد زیادی پیامک است. آیا اجازه میدهید این برنامه همچنان پیامک ارسال کند؟"</string> <string name="sms_control_yes" msgid="4858845109269524622">"مجاز است"</string> - <string name="sms_control_no" msgid="4845717880040355570">"اجازه ندارد"</string> + <string name="sms_control_no" msgid="4845717880040355570">"مجاز نبودن"</string> <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> مایل است پیامی به <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> ارسال کند."</string> <string name="sms_short_code_details" msgid="2723725738333388351">"این مورد "<b>"شاید هزینهای"</b>" را به حساب دستگاه همراهتان بگذارد."</string> <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"این مورد هزینهای را به حساب دستگاه همراهتان میگذارد."</b></string> @@ -1410,7 +1410,7 @@ <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"میخواهید به این درخواست اجازه دهید؟"</string> <string name="grant_permissions_header_text" msgid="3420736827804657201">"درخواست دسترسی"</string> <string name="allow" msgid="6195617008611933762">"ارزیابیشده"</string> - <string name="deny" msgid="6632259981847676572">"اجازه ندارد"</string> + <string name="deny" msgid="6632259981847676572">"مجاز نبودن"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"مجوز درخواست شد"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"مجوز\nبرای حساب <xliff:g id="ACCOUNT">%s</xliff:g> درخواست شد."</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"شما از این برنامه در خارج از نمایه کاریتان استفاده میکنید"</string> @@ -1639,7 +1639,7 @@ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"مشاهده و انجام کنشها"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"این عملکرد میتواند با برنامه یا حسگری سختافزاری تعاملاتتان را ردیابی کند و ازطرف شما با برنامهها تعامل داشته باشد."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"مجاز"</string> - <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"رد کردن"</string> + <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"مجاز نبودن"</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن ضربه بزنید:"</string> <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگیهای موردنظر برای استفاده با دکمه دسترسپذیری"</string> <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگیهای موردنظر برای استفاده با میانبر کلید میزان صدا"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index e5600b0e414d..b7a332efed21 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -849,7 +849,7 @@ <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausar"</string> <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Reproducir"</string> <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Deter"</string> - <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Rebobinar"</string> + <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Retroceder"</string> <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Avance rápido"</string> <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 5ba54d3ab9e2..6726be21ae67 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1135,7 +1135,7 @@ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"ઍક્સેસ આપો"</string> <string name="whichEditApplication" msgid="6191568491456092812">"આનાથી સંપાદિત કરો"</string> <string name="whichEditApplicationNamed" msgid="8096494987978521514">"%1$s સાથે સંપાદિત કરો"</string> - <string name="whichEditApplicationLabel" msgid="1463288652070140285">"સંપાદિત કરો"</string> + <string name="whichEditApplicationLabel" msgid="1463288652070140285">"ફેરફાર કરો"</string> <string name="whichSendApplication" msgid="4143847974460792029">"શેર કરો"</string> <string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$s સાથે શેર કરો"</string> <string name="whichSendApplicationLabel" msgid="7467813004769188515">"શેર કરો"</string> @@ -1221,7 +1221,7 @@ <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"સાઇલેન્ટ રિંગટોન સેટ કરી"</string> <string name="volume_call" msgid="7625321655265747433">"ઇન-કૉલ વૉલ્યૂમ"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"બ્લૂટૂથ ઇન-કૉલ વૉલ્યૂમ"</string> - <string name="volume_alarm" msgid="4486241060751798448">"એલાર્મ વૉલ્યૂમ"</string> + <string name="volume_alarm" msgid="4486241060751798448">"અલાર્મ વૉલ્યૂમ"</string> <string name="volume_notification" msgid="6864412249031660057">"સૂચના વૉલ્યૂમ"</string> <string name="volume_unknown" msgid="4041914008166576293">"વૉલ્યૂમ"</string> <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"બ્લૂટૂથ વૉલ્યૂમ"</string> @@ -1513,7 +1513,7 @@ <string name="storage_usb_drive" msgid="448030813201444573">"USB ડ્રાઇવ"</string> <string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ડ્રાઇવ"</string> <string name="storage_usb" msgid="2391213347883616886">"USB સ્ટોરેજ"</string> - <string name="extract_edit_menu_button" msgid="63954536535863040">"સંપાદિત કરો"</string> + <string name="extract_edit_menu_button" msgid="63954536535863040">"ફેરફાર કરો"</string> <string name="data_usage_warning_title" msgid="9034893717078325845">"ડેટા ચેતવણી"</string> <string name="data_usage_warning_body" msgid="1669325367188029454">"તમે <xliff:g id="APP">%s</xliff:g> ડેટા વાપર્યો છે"</string> <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"મોબાઇલ ડેટાની મર્યાદા આવી ગઈ"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index b66d7cb3136e..34e1db5ef4ce 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -307,7 +307,7 @@ <string name="permgroupdesc_microphone" msgid="1047786732792487722">"hanganyag rögzítése"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Testmozgás"</string> <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"hozzáférés a testmozgási adatokhoz"</string> - <string name="permgrouplab_camera" msgid="9090413408963547706">"Fényképezőgép"</string> + <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string> <string name="permgroupdesc_camera" msgid="7585150538459320326">"fotók és videók készítése"</string> <string name="permgrouplab_calllog" msgid="7926834372073550288">"Hívásnaplók"</string> <string name="permgroupdesc_calllog" msgid="2026996642917801803">"hívásnapló olvasása és írása"</string> @@ -1900,8 +1900,8 @@ <string name="profile_encrypted_message" msgid="1128512616293157802">"A feloldáshoz koppintson rá"</string> <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Csatlakoztatva a(z) <xliff:g id="PRODUCT_NAME">%1$s</xliff:g> eszközhöz"</string> <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Koppintson ide a fájlok megtekintéséhez"</string> - <string name="pin_target" msgid="8036028973110156895">"Rögzítés"</string> - <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítése"</string> + <string name="pin_target" msgid="8036028973110156895">"Kitűzés"</string> + <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> kitűzése"</string> <string name="unpin_target" msgid="3963318576590204447">"Feloldás"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítésének feloldása"</string> <string name="app_info" msgid="6113278084877079851">"Alkalmazásinformáció"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index b531d02998e4..e0b809f97323 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -48,7 +48,7 @@ <string name="invalidPin" msgid="7542498253319440408">"Մուտքագրեք PIN, որը 4-ից 8 թիվ է:"</string> <string name="invalidPuk" msgid="8831151490931907083">"Մուտքագրեք PUK, որն 8 կամ ավել թիվ ունի:"</string> <string name="needPuk" msgid="7321876090152422918">"Ձեր SIM քարտը PUK-ով կողպված է: Մուտքագրեք PUK կոդը այն ապակողպելու համար:"</string> - <string name="needPuk2" msgid="7032612093451537186">"Մուտքագրեք PUK2-ը` SIM քարտն արգելահանելու համար:"</string> + <string name="needPuk2" msgid="7032612093451537186">"Մուտքագրեք PUK2-ը՝ SIM քարտն արգելահանելու համար:"</string> <string name="enablePin" msgid="2543771964137091212">"Ձախողվեց: Միացրեք SIM/RUIM կողպումը:"</string> <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584"> <item quantity="one">Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն արգելափակվելու է:</item> @@ -409,7 +409,7 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Թույլ է տալիս հավելվածին փոփոխել ձեր պլանշետի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string> <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Թույլ է տալիս հավելվածին փոփոխել Android TV սարքի զանգերի մատյանը, այդ թվում՝ մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել՝ ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string> <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Թույլ է տալիս հավելվածին փոփոխել ձեր հեռախոսի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string> - <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ` սրտի կծկումների հաճախականության չափիչ)"</string> + <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ՝ սրտի կծկումների հաճախականության չափիչ)"</string> <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Հավելվածին թույլ է տալիս մուտք ունենալ սենսորների տվյալներին, որոնք վերահսկում են ձեր ֆիզիկական վիճակը, օրինակ՝ ձեր սրտի զարկերը:"</string> <string name="permlab_readCalendar" msgid="6408654259475396200">"Կարդալ օրացույցի միջոցառումները և տվյալները"</string> <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Այս հավելվածը կարող է կարդալ օրացույցի՝ ձեր պլանշետում պահված բոլոր միջոցառումները, ինչպես նաև հրապարակել կամ պահել ձեր օրացույցի տվյալները:"</string> @@ -428,7 +428,7 @@ <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"տեղադրության մասին տվյալների հասանելիություն ֆոնային ռեժիմում"</string> <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Այս հավելվածը ցանկացած ժամանակ կարող է տեսնել տեղադրության տվյալները, նույնիսկ երբ այն ակտիվ չէ։"</string> <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"փոխել ձեր աուդիո կարգավորումները"</string> - <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ` ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string> + <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ՝ ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"ձայնագրել աուդիո ֆայլ"</string> <string name="permdesc_recordAudio" msgid="3976213377904701093">"Այս հավելվածը ցանկացած պահի կարող է ձայնագրել խոսափողի օգնությամբ:"</string> <string name="permlab_sim_communication" msgid="176788115994050692">"ուղարկել հրամաններ SIM քարտին"</string> @@ -493,7 +493,7 @@ <string name="permlab_changeTetherState" msgid="9079611809931863861">"փոխել միացված կապը"</string> <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Թույլ է տալիս հավելվածին փոխել կապված ցանցի միացման կարգավիճակը:"</string> <string name="permlab_accessWifiState" msgid="5552488500317911052">"դիտել Wi-Fi կապերը"</string> - <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Թույլ է տալիս հավելվածին տեսնել Wi-Fi ցանցի տեղեկություններ, ինչպես օրինակ` արդյոք Wi-Fi-ը միացված է, թե` ոչ, և միացված Wi-Fi սարքի անունը:"</string> + <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Թույլ է տալիս հավելվածին տեսնել Wi-Fi ցանցի տեղեկություններ, ինչպես օրինակ՝ արդյոք Wi-Fi-ը միացված է, թե` ոչ, և միացված Wi-Fi սարքի անունը:"</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"միանալ Wi-Fi-ին և անջատվել դրանից"</string> <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Թույլ է տալիս հավելվածին միանալ Wi-Fi մուտքի կետերին և անջատվել այդ կետերից, ինչպես նաև կատարել սարքի կարգավորման փոփոխություններ Wi-Fi ցանցերի համար:"</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"թույլատրել Բազմասփյուռ Wi-Fi-ի ընդունումը"</string> @@ -612,7 +612,7 @@ </string-array> <string name="face_icon_content_description" msgid="465030547475916280">"Դեմքի պատկերակ"</string> <string name="permlab_readSyncSettings" msgid="6250532864893156277">"կարդալ համաժամացման կարգավորումները"</string> - <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Թույլ է տալիս հավելվածին կարդալ համաժամացման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամացված է հաշվի հետ:"</string> + <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Թույլ է տալիս հավելվածին կարդալ համաժամացման կարգավորումները հաշվի համար: Օրինակ՝ այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամացված է հաշվի հետ:"</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"համաժամացումը փոխարկել միացվածի և անջատվածի"</string> <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Թույլ է տալիս հավելվածին փոփոխել համաժամացման կարգավորումները հաշվի համար: Օրինակ, այն կարող է օգտագործվել` միացնելու Մարդիկ հավելվածի համաժամացումը հաշվի հետ:"</string> <string name="permlab_readSyncStats" msgid="3747407238320105332">"կարդալ համաժամացման վիճակագրությունը"</string> @@ -873,7 +873,7 @@ <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Մոռացե՞լ եք սխեման:"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Հաշվի ապակողպում"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Չափից շատ սխեմայի փորձեր"</string> - <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ապակողպելու համար` մուտք գործեք ձեր Google հաշվով:"</string> + <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ապակողպելու համար՝ մուտք գործեք ձեր Google հաշվով:"</string> <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Օգտանուն (էլփոստ)"</string> <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Գաղտնաբառ"</string> <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Մուտք գործել"</string> @@ -1404,7 +1404,7 @@ <string name="ime_action_done" msgid="6299921014822891569">"Պատրաստ է"</string> <string name="ime_action_previous" msgid="6548799326860401611">"Նախորդ"</string> <string name="ime_action_default" msgid="8265027027659800121">"Կատարել"</string> - <string name="dial_number_using" msgid="6060769078933953531">"Հավաքել հեռախոսահամարը`\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string> + <string name="dial_number_using" msgid="6060769078933953531">"Հավաքել հեռախոսահամարը՝\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string> <string name="create_contact_using" msgid="6200708808003692594">"Ստեղծել կոնտակտ`\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string> <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Հետևյալ մեկ կամ մի քանի հավելվածներին թույլտվություն է անհրաժեշտ՝ այժմ և հետագայում ձեր հաշվի տվյալներն օգտագործելու համար։"</string> <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Թույլատրե՞լ"</string> @@ -1474,7 +1474,7 @@ <string name="number_picker_increment_button" msgid="7621013714795186298">"Ավելացնել"</string> <string name="number_picker_decrement_button" msgid="5116948444762708204">"Նվազեցնել"</string> <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> հպեք և պահեք:"</string> - <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Սահեցրեք վերև` ավելացնելու համար, և ներքև` նվազեցնելու համար:"</string> + <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Սահեցրեք վերև՝ ավելացնելու համար, և ներքև՝ նվազեցնելու համար:"</string> <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Աճեցնել րոպեն"</string> <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Նվազեցնել րոպեն"</string> <string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Աճեցնել ժամը"</string> @@ -1598,7 +1598,7 @@ <string name="kg_invalid_puk" msgid="4809502818518963344">"Վերամուտքագրեք ճիշտ PUK ծածկագիրը: Կրկնվող փորձերը ընդմիշտ կկասեցնեն SIM քարտը:"</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN ծածկագրերը չեն համընկնում"</string> <string name="kg_login_too_many_attempts" msgid="699292728290654121">"Չափից շատ սխեմայի փորձեր"</string> - <string name="kg_login_instructions" msgid="3619844310339066827">"Ապակողպելու համար` մուտք գործեք ձեր Google հաշվով:"</string> + <string name="kg_login_instructions" msgid="3619844310339066827">"Ապակողպելու համար՝ մուտք գործեք ձեր Google հաշվով:"</string> <string name="kg_login_username_hint" msgid="1765453775467133251">"Օգտանուն (էլփոստ)"</string> <string name="kg_login_password_hint" msgid="3330530727273164402">"Գաղտնաբառը"</string> <string name="kg_login_submit_button" msgid="893611277617096870">"Մուտք գործել"</string> @@ -1979,7 +1979,7 @@ <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Չհաջողվեց վերականգնել դյուրանցումը, քանի որ հավելվածների ստորագրությունները տարբեր են"</string> <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Չհաջողվեց վերականգնել դյուրանցումը"</string> <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Դյուրանցումն անջատված է"</string> - <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ՀԵՌԱՑՆԵԼ"</string> + <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ԱՊԱՏԵՂԱԴՐԵԼ"</string> <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ԲԱՑԵԼ"</string> <string name="harmful_app_warning_title" msgid="8794823880881113856">"Հայտնաբերվել է վնասաբեր հավելված"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index b9e7f4f8c955..7a36d13fbe9d 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1231,7 +1231,7 @@ <string name="volume_icon_description_notification" msgid="579091344110747279">"Volume pemberitahuan"</string> <string name="ringtone_default" msgid="9118299121288174597">"Nada dering default"</string> <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Default (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> - <string name="ringtone_silent" msgid="397111123930141876">"Tidak Ada"</string> + <string name="ringtone_silent" msgid="397111123930141876">"Tidak ada"</string> <string name="ringtone_picker_title" msgid="667342618626068253">"Nada dering"</string> <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Suara alarm"</string> <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Suara notifikasi"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 692547b2d581..61754b25f4f4 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1792,7 +1792,7 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Battery Saver:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Батареяны үнемдеу режимі:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string> <string name="battery_saver_description" msgid="6794188153647295212">"Батарея ұзағырақ жұмыс істеуі үшін, Battery Saver:\n\n• қараңғы тақырыпты қосады;\n•фондық жұмысты, кейбір визуалды әсерлерді және \"Ok Google\" сияқты басқа функцияларды өшіреді не шектейді."</string> <string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Data Saver функциясы кейбір қолданбаларға деректерді фондық режимде жіберуге және алуға жол бермейді. Ашық тұрған қолданба деректерді пайдаланады, бірақ шектеулі шамада (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Data Saver функциясын қосу керек пе?"</string> @@ -1912,7 +1912,7 @@ <string name="conference_call" msgid="5731633152336490471">"Конференциялық қоңырау"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"Қалқыма сөзкөмек"</string> <string name="app_category_game" msgid="4534216074910244790">"Ойындар"</string> - <string name="app_category_audio" msgid="8296029904794676222">"Музыка және аудиомазмұн"</string> + <string name="app_category_audio" msgid="8296029904794676222">"Музыка және аудио"</string> <string name="app_category_video" msgid="2590183854839565814">"Фильм және бейне"</string> <string name="app_category_image" msgid="7307840291864213007">"Суреттер және кескіндер"</string> <string name="app_category_social" msgid="2278269325488344054">"Әлеуметтік қолданба мен байланыс"</string> @@ -2000,7 +2000,7 @@ <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режим туралы хабарландыру"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея заряды азаюы мүмкін"</string> <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарея ұзаққа жетуі үшін, Battery Saver іске қосылды"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Battery Saver"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Батареяны үнемдеу режимі"</string> <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Battery Saver өшірілді"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Телефонның заряды жеткілікті. Функцияларға енді шектеу қойылмайды."</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Планшеттің заряды жеткілікті. Функцияларға енді шектеу қойылмайды."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index b939c1ac21ea..738e984aed49 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2000,7 +2000,7 @@ <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ការជូនដំណឹងព័ត៌មានរបស់មុខងារទម្លាប់"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ថ្មអាចនឹងអស់ មុនពេលសាកថ្មធម្មតា"</string> <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បានបើកដំណើរការកម្មវិធីសន្សំថ្ម ដើម្បីបង្កើនកម្រិតថាមពលថ្ម"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"កម្មវិធីសន្សំថ្ម"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"មុខងារសន្សំថ្ម"</string> <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"កម្មវិធីសន្សំថ្មត្រូវបានបិទ"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ទូរសព្ទមានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"ថេប្លេតមានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index edc4de685334..1517c17d9615 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1317,7 +1317,7 @@ <string name="console_running_notification_title" msgid="6087888939261635904">"Сериялык консоль иштетилди"</string> <string name="console_running_notification_message" msgid="7892751888125174039">"Майнаптуулугуна таасири тиет. Аны өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string> <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB портунда суюктук же урандылар бар"</string> - <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string> + <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчтү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string> <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"USB портун колдонууга болот"</string> <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Телефон суюктук менен урандыларды аныктаган жок."</string> <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Мүчүлүштүк тууралуу кабар алынууда…"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 6af5d5ee87ca..3833605fa78b 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1117,7 +1117,7 @@ <string name="yes" msgid="9069828999585032361">"ОК"</string> <string name="no" msgid="5122037903299899715">"Цуцлах"</string> <string name="dialog_alert_title" msgid="651856561974090712">"Анхаар"</string> - <string name="loading" msgid="3138021523725055037">"Ачааллаж байна..."</string> + <string name="loading" msgid="3138021523725055037">"Ачаалж байна..."</string> <string name="capital_on" msgid="2770685323900821829">"Идэвхтэй"</string> <string name="capital_off" msgid="7443704171014626777">"Идэвхгүй"</string> <string name="checked" msgid="9179896827054513119">"тэмдэглэсэн"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 95d5f8f3ab64..bae44fd801ba 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -711,7 +711,7 @@ <item msgid="6216981255272016212">"Aangepast"</item> </string-array> <string-array name="emailAddressTypes"> - <item msgid="7786349763648997741">"Thuis"</item> + <item msgid="7786349763648997741">"Privé"</item> <item msgid="435564470865989199">"Werk"</item> <item msgid="4199433197875490373">"Overig"</item> <item msgid="3233938986670468328">"Aangepast"</item> @@ -769,7 +769,7 @@ <string name="eventTypeAnniversary" msgid="4684702412407916888">"Jubileum"</string> <string name="eventTypeOther" msgid="530671238533887997">"Overig"</string> <string name="emailTypeCustom" msgid="1809435350482181786">"Aangepast"</string> - <string name="emailTypeHome" msgid="1597116303154775999">"Thuis"</string> + <string name="emailTypeHome" msgid="1597116303154775999">"Privé"</string> <string name="emailTypeWork" msgid="2020095414401882111">"Werk"</string> <string name="emailTypeOther" msgid="5131130857030897465">"Overig"</string> <string name="emailTypeMobile" msgid="787155077375364230">"Mobiel"</string> @@ -931,9 +931,9 @@ <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nWeet u zeker dat u deze pagina wilt verlaten?"</string> <string name="save_password_label" msgid="9161712335355510035">"Bevestigen"</string> <string name="double_tap_toast" msgid="7065519579174882778">"Tip: dubbeltik om in en uit te zoomen."</string> - <string name="autofill_this_form" msgid="3187132440451621492">"Autom. aanvullen"</string> - <string name="setup_autofill" msgid="5431369130866618567">"Autom. aanvullen instellen"</string> - <string name="autofill_window_title" msgid="4379134104008111961">"Automatisch aanvullen met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> + <string name="autofill_this_form" msgid="3187132440451621492">"Autom. invullen"</string> + <string name="setup_autofill" msgid="5431369130866618567">"Autom. invullen instellen"</string> + <string name="autofill_window_title" msgid="4379134104008111961">"Automatisch invullen met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> <string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string> <string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="760522655085707045">", "</string> @@ -1101,7 +1101,7 @@ <string name="selectTextMode" msgid="3225108910999318778">"Tekst selecteren"</string> <string name="undo" msgid="3175318090002654673">"Ongedaan maken"</string> <string name="redo" msgid="7231448494008532233">"Opnieuw"</string> - <string name="autofill" msgid="511224882647795296">"Automatisch aanvullen"</string> + <string name="autofill" msgid="511224882647795296">"Automatisch invullen"</string> <string name="textSelectionCABTitle" msgid="5151441579532476940">"Tekstselectie"</string> <string name="addToDictionary" msgid="8041821113480950096">"Toevoegen aan woordenboek"</string> <string name="deleteText" msgid="4200807474529938112">"Verwijderen"</string> @@ -1276,7 +1276,7 @@ <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nooit toestaan"</string> <string name="sim_removed_title" msgid="5387212933992546283">"Simkaart verwijderd"</string> <string name="sim_removed_message" msgid="9051174064474904617">"Het mobiele netwerk is niet beschikbaar totdat u het apparaat opnieuw start met een geldige simkaart."</string> - <string name="sim_done_button" msgid="6464250841528410598">"Gereed"</string> + <string name="sim_done_button" msgid="6464250841528410598">"Klaar"</string> <string name="sim_added_title" msgid="7930779986759414595">"Simkaart aangesloten"</string> <string name="sim_added_message" msgid="6602906609509958680">"Start je apparaat opnieuw voor toegang tot het mobiele netwerk."</string> <string name="sim_restart_button" msgid="8481803851341190038">"Opnieuw starten"</string> @@ -1289,7 +1289,7 @@ <string name="time_picker_dialog_title" msgid="9053376764985220821">"Tijd instellen"</string> <string name="date_picker_dialog_title" msgid="5030520449243071926">"Datum instellen"</string> <string name="date_time_set" msgid="4603445265164486816">"Instellen"</string> - <string name="date_time_done" msgid="8363155889402873463">"Gereed"</string> + <string name="date_time_done" msgid="8363155889402873463">"Klaar"</string> <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NIEUW: "</font></string> <string name="perms_description_app" msgid="2747752389870161996">"Geleverd door <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="no_permissions" msgid="5729199278862516390">"Geen rechten nodig"</string> @@ -1376,7 +1376,7 @@ <string name="ext_media_status_removed" msgid="241223931135751691">"Verwijderd"</string> <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Uitgeworpen"</string> <string name="ext_media_status_checking" msgid="159013362442090347">"Controleren…"</string> - <string name="ext_media_status_mounted" msgid="3459448555811203459">"Gereed"</string> + <string name="ext_media_status_mounted" msgid="3459448555811203459">"Klaar"</string> <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Alleen lezen"</string> <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Onveilig verwijderd"</string> <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Beschadigd"</string> @@ -1401,7 +1401,7 @@ <string name="ime_action_search" msgid="4501435960587287668">"Zoeken"</string> <string name="ime_action_send" msgid="8456843745664334138">"Verzenden"</string> <string name="ime_action_next" msgid="4169702997635728543">"Volgende"</string> - <string name="ime_action_done" msgid="6299921014822891569">"Gereed"</string> + <string name="ime_action_done" msgid="6299921014822891569">"Klaar"</string> <string name="ime_action_previous" msgid="6548799326860401611">"Vorige"</string> <string name="ime_action_default" msgid="8265027027659800121">"Uitvoeren"</string> <string name="dial_number_using" msgid="6060769078933953531">"Nummer bellen\nmet <xliff:g id="NUMBER">%s</xliff:g>"</string> @@ -1448,7 +1448,7 @@ <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> van <xliff:g id="TOTAL">%d</xliff:g></item> <item quantity="one">1 overeenkomst</item> </plurals> - <string name="action_mode_done" msgid="2536182504764803222">"Gereed"</string> + <string name="action_mode_done" msgid="2536182504764803222">"Klaar"</string> <string name="progress_erasing" msgid="6891435992721028004">"Gedeelde opslag wissen…"</string> <string name="share" msgid="4157615043345227321">"Delen"</string> <string name="find" msgid="5015737188624767706">"Vinden"</string> @@ -1492,7 +1492,7 @@ <string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string> <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Annuleren"</string> <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Delete"</string> - <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Gereed"</string> + <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Klaar"</string> <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Modus wijzigen"</string> <string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string> <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string> @@ -1645,7 +1645,7 @@ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string> <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgeschakeld"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string> - <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gereed"</string> + <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string> <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Sneltoets uitschakelen"</string> <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string> @@ -1774,7 +1774,7 @@ <string name="immersive_cling_title" msgid="2307034298721541791">"Volledig scherm wordt weergegeven"</string> <string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string> <string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string> - <string name="done_label" msgid="7283767013231718521">"Gereed"</string> + <string name="done_label" msgid="7283767013231718521">"Klaar"</string> <string name="hour_picker_description" msgid="5153757582093524635">"Ronde schuifregelaar voor uren"</string> <string name="minute_picker_description" msgid="9029797023621927294">"Ronde schuifregelaar voor minuten"</string> <string name="select_hours" msgid="5982889657313147347">"Uren selecteren"</string> @@ -1928,13 +1928,13 @@ <string name="time_picker_prompt_label" msgid="303588544656363889">"Typ een tijd"</string> <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Schakel naar de tekstinvoermodus om de tijd in te voeren."</string> <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Schakel naar de klokmodus om de tijd in te voeren."</string> - <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opties voor automatisch aanvullen"</string> - <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Opslaan voor Automatisch aanvullen"</string> + <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opties voor automatisch invullen"</string> + <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Opslaan voor Automatisch invullen"</string> <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Content kan niet automatisch worden aangevuld"</string> - <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Geen suggesties van Automatisch aanvullen"</string> + <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Geen suggesties van Automatisch invullen"</string> <plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978"> - <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch aanvullen</item> - <item quantity="one">Eén suggestie van Automatisch aanvullen</item> + <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch invullen</item> + <item quantity="one">Eén suggestie van Automatisch invullen</item> </plurals> <string name="autofill_save_title" msgid="7719802414283739775">"Opslaan in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> <string name="autofill_save_title_with_type" msgid="3002460014579799605">"<xliff:g id="TYPE">%1$s</xliff:g> opslaan in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index dc63de6bbc7a..b9d041ec67f6 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -896,7 +896,7 @@ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"ਅਣਲਾਕ ਖੇਤਰ ਨਸ਼ਟ ਕੀਤਾ।"</string> <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ਵਿਜੇਟ।"</string> <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"ਉਪਭੋਗਤਾ ਚੋਣਕਾਰ"</string> - <string name="keyguard_accessibility_status" msgid="6792745049712397237">"ਅਵਸਥਾ"</string> + <string name="keyguard_accessibility_status" msgid="6792745049712397237">"ਸਥਿਤੀ"</string> <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"ਕੈਮਰਾ"</string> <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"ਮੀਡੀਆ ਨਿਯੰਤਰਣ"</string> <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"ਵਿਜੇਟ ਨੂੰ ਪੁਨਰ ਤਰਤੀਬ ਦੇਣਾ ਸ਼ੁਰੂ ਹੋਇਆ।"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 30d3afead0b6..955e9a15a7dd 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -241,7 +241,7 @@ <string name="global_action_power_off" msgid="4404936470711393203">"Искључи"</string> <string name="global_action_power_options" msgid="1185286119330160073">"Напајање"</string> <string name="global_action_restart" msgid="4678451019561687074">"Рестартуј"</string> - <string name="global_action_emergency" msgid="1387617624177105088">"Хитни позив"</string> + <string name="global_action_emergency" msgid="1387617624177105088">"Хитан позив"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"Извештај о грешци"</string> <string name="global_action_logout" msgid="6093581310002476511">"Заврши сесију"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"Снимак екрана"</string> @@ -832,7 +832,7 @@ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string> - <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитни позив"</string> + <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитан позив"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string> @@ -1815,7 +1815,7 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string> <string name="battery_saver_description" msgid="6794188153647295212">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“"</string> <string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index fe647cb70d4c..279c619c65fc 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1829,7 +1829,7 @@ <item quantity="other">I %d tim</item> <item quantity="one">I en 1 tim</item> </plurals> - <string name="zen_mode_until" msgid="2250286190237669079">"Till kl. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> + <string name="zen_mode_until" msgid="2250286190237669079">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> <string name="zen_mode_alarm" msgid="7046911727540499275">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string> <string name="zen_mode_forever" msgid="740585666364912448">"Tills du stänger av"</string> <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Tills du inaktiverar Stör ej"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 8276905e2ef0..e168ae44a2d0 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -303,7 +303,7 @@ <string name="permgroupdesc_sms" msgid="5726462398070064542">"itume na iangalie SMS"</string> <string name="permgrouplab_storage" msgid="1938416135375282333">"Faili na maudhui"</string> <string name="permgroupdesc_storage" msgid="6351503740613026600">"ifikie picha, maudhui na faili kwenye kifaa chako"</string> - <string name="permgrouplab_microphone" msgid="2480597427667420076">"Kipokea sauti"</string> + <string name="permgrouplab_microphone" msgid="2480597427667420076">"Maikrofoni"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"irekodi sauti"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Shughuli za kimwili"</string> <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ifikie shughuli zako za kimwili"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index aeeb89769893..f7265536b7d9 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2029,7 +2029,7 @@ <item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> และอีก <xliff:g id="COUNT_3">%d</xliff:g> ไฟล์</item> <item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> และอีก <xliff:g id="COUNT_1">%d</xliff:g> ไฟล์</item> </plurals> - <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่มีบุคคลที่แนะนำให้แชร์ด้วย"</string> + <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่พบใครที่แนะนำให้แชร์ด้วย"</string> <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"รายชื่อแอป"</string> <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"แอปนี้ไม่ได้รับอนุญาตให้บันทึกเสียงแต่จะบันทึกเสียงผ่านอุปกรณ์ USB นี้ได้"</string> <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"หน้าแรก"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 173a8ac900f1..05e00f2a2b6f 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1793,7 +1793,7 @@ <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi\n\n"<annotation id="url">"Batafsil"</annotation></string> - <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi"</string> + <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi"</string> <string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a1723c0716de..550601af0faa 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2256,7 +2256,7 @@ <!-- Amount of time in ms the user needs to press the relevant keys to trigger the screenshot chord --> - <integer name="config_screenshotChordKeyTimeout">500</integer> + <integer name="config_screenshotChordKeyTimeout">0</integer> <!-- Default width of a vertical scrollbar and height of a horizontal scrollbar. Takes effect only if the scrollbar drawables have no intrinsic size. --> @@ -2781,6 +2781,7 @@ <item>power</item> <item>restart</item> <item>logout</item> + <item>screenshot</item> <item>bugreport</item> </string-array> @@ -3484,6 +3485,7 @@ mode --> <string-array translatable="false" name="config_priorityOnlyDndExemptPackages"> <item>com.android.dialer</item> + <item>com.android.server.telecom</item> <item>com.android.systemui</item> <item>android</item> </string-array> diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index f11adef81793..7d2e32ab08d3 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -191,7 +191,7 @@ public class TransactionParcelTests { PersistableBundle persistableBundle = new PersistableBundle(); persistableBundle.putInt("k", 4); FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments( - Surface.ROTATION_90, DisplayCutout.NO_CUTOUT); + Surface.ROTATION_90, 1920, 1080, DisplayCutout.NO_CUTOUT); LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, config(), overrideConfig, compat, referrer, null /* voiceInteractor */, @@ -351,7 +351,8 @@ public class TransactionParcelTests { ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(), null /* activityToken */); transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(), - new FixedRotationAdjustments(Surface.ROTATION_270, DisplayCutout.NO_CUTOUT))); + new FixedRotationAdjustments(Surface.ROTATION_270, 1920, 1080, + DisplayCutout.NO_CUTOUT))); writeAndPrepareForReading(transaction); diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java index 2fc42e91a8cc..3cf1722d49d3 100644 --- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java +++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java @@ -77,8 +77,10 @@ public class DisplayAdjustmentsTests { final int realRotation = Surface.ROTATION_0; final int fixedRotation = Surface.ROTATION_90; - mDisplayAdjustments.setFixedRotationAdjustments( - new FixedRotationAdjustments(fixedRotation, null /* cutout */)); + final int appWidth = 1080; + final int appHeight = 1920; + mDisplayAdjustments.setFixedRotationAdjustments(new FixedRotationAdjustments( + fixedRotation, appWidth, appHeight, null /* cutout */)); final int w = 1000; final int h = 2000; @@ -95,13 +97,21 @@ public class DisplayAdjustmentsTests { metrics.heightPixels = metrics.noncompatHeightPixels = h; final DisplayMetrics flippedMetrics = new DisplayMetrics(); - flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = h; + // The physical dpi should not be adjusted. + flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = w; flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h; - flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = w; + flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = h; flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w; mDisplayAdjustments.adjustMetrics(metrics, realRotation); assertEquals(flippedMetrics, metrics); + + mDisplayAdjustments.adjustGlobalAppMetrics(metrics); + + assertEquals(appWidth, metrics.widthPixels); + assertEquals(appWidth, metrics.noncompatWidthPixels); + assertEquals(appHeight, metrics.heightPixels); + assertEquals(appHeight, metrics.noncompatHeightPixels); } } diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index c7d835ca7c7e..576cd78606d7 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -124,6 +124,24 @@ public class InsetsStateTest { } @Test + public void testCalculateInsets_extraNavRightClimateTop() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_CLIMATE_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_CLIMATE_BAR).setVisible(true); + mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); + mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, + false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null); + // ITYPE_CLIMATE_BAR is a type of status bar and ITYPE_EXTRA_NAVIGATION_BAR is a type + // of navigation bar. + assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); + assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars())); + assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars())); + } + } + + @Test public void testCalculateInsets_imeIgnoredWithoutAdjustResize() { try (final InsetsModeSession session = new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { @@ -331,6 +349,8 @@ public class InsetsStateTest { public void testGetDefaultVisibility() { assertTrue(InsetsState.getDefaultVisibility(ITYPE_STATUS_BAR)); assertTrue(InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR)); + assertTrue(InsetsState.getDefaultVisibility(ITYPE_CLIMATE_BAR)); + assertTrue(InsetsState.getDefaultVisibility(ITYPE_EXTRA_NAVIGATION_BAR)); assertTrue(InsetsState.getDefaultVisibility(ITYPE_CAPTION_BAR)); assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME)); } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 5c16772488d0..4cf6715ba0ca 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -24,6 +24,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -117,7 +118,15 @@ public class ViewRootImplTest { final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); - // A window which fits system bars must fit IME, unless its type is toast or system alert. + assertEquals(Type.systemBars(), attrs.getFitInsetsTypes()); + } + + @Test + public void adjustLayoutParamsForCompatibility_fitSystemBarsAndIme() { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); + attrs.softInputMode |= SOFT_INPUT_ADJUST_RESIZE; + ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); + assertEquals(Type.systemBars() | Type.ime(), attrs.getFitInsetsTypes()); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index e08e42aec391..950666b81003 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -168,6 +168,7 @@ applications that come with the platform <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS" /> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> <permission name="android.permission.PERFORM_CDMA_PROVISIONING"/> @@ -224,6 +225,7 @@ applications that come with the platform <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.UPDATE_DEVICE_STATS"/> + <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> </privapp-permissions> <privapp-permissions package="com.android.providers.media.module"> diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp index 4f4364f72fef..7fbbb61e469d 100644 --- a/libs/WindowManager/Jetpack/Android.bp +++ b/libs/WindowManager/Jetpack/Android.bp @@ -24,14 +24,14 @@ java_library { static_libs: ["window-sidecar"], installable: true, sdk_version: "core_platform", - vendor: true, + system_ext_specific: true, libs: ["framework", "androidx.annotation_annotation",], required: ["androidx.window.sidecar.xml",], } prebuilt_etc { name: "androidx.window.sidecar.xml", - vendor: true, + system_ext_specific: true, sub_dir: "permissions", src: "androidx.window.sidecar.xml", filename_from_src: true, diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml index f88a5f4ae039..359e69fd9bc1 100644 --- a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml +++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml @@ -17,5 +17,5 @@ <permissions> <library name="androidx.window.sidecar" - file="/vendor/framework/androidx.window.sidecar.jar"/> + file="/system_ext/framework/androidx.window.sidecar.jar"/> </permissions> diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index a362bd220936..667a7517a24c 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -139,7 +139,7 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { +static void setBufferCount(ANativeWindow* window) { int query_value; int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { @@ -148,7 +148,9 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { } auto min_undequeued_buffers = static_cast<uint32_t>(query_value); - int bufferCount = min_undequeued_buffers + 2 + extraBuffers; + // We only need to set min_undequeued + 2 because the renderahead amount was already factored into the + // query for min_undequeued + int bufferCount = min_undequeued_buffers + 2; native_window_set_buffer_count(window, bufferCount); } @@ -179,7 +181,8 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior); if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) { - setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity); + setBufferCount(mNativeSurface->getNativeWindow()); + } mFrameNumber = -1; diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 75ea0cbada92..7ed5b579ebb4 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -45,9 +45,9 @@ import com.android.internal.location.ProviderProperties; interface ILocationManager { Location getLastLocation(in LocationRequest request, String packageName, String featureId); - boolean getCurrentLocation(in LocationRequest request, - in ICancellationSignal cancellationSignal, in ILocationListener listener, - String packageName, String featureId, String listenerId); + @nullable ICancellationSignal getCurrentLocation(in LocationRequest request, + in ILocationListener listener, String packageName, String featureId, + String listenerId); void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, in PendingIntent intent, String packageName, String featureId, String listenerId); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 05a9863cb266..b77a249d0fe9 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -726,16 +726,15 @@ public class LocationManager { cancellationSignal.throwIfCanceled(); } - ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport(); - try { - if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal, - transport, mContext.getPackageName(), mContext.getAttributionTag(), - transport.getListenerId())) { + ICancellationSignal cancelRemote = mService.getCurrentLocation( + currentLocationRequest, transport, mContext.getPackageName(), + mContext.getAttributionTag(), transport.getListenerId()); + if (cancelRemote != null) { transport.register(mContext.getSystemService(AlarmManager.class), - remoteCancellationSignal); + cancellationSignal, cancelRemote); if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener(transport::cancel); + cancellationSignal.setRemote(cancelRemote); } } else { transport.fail(); @@ -2540,7 +2539,7 @@ public class LocationManager { } private static class GetCurrentLocationTransport extends ILocationListener.Stub implements - AlarmManager.OnAlarmListener { + AlarmManager.OnAlarmListener, CancellationSignal.OnCancelListener { @GuardedBy("this") @Nullable @@ -2572,6 +2571,7 @@ public class LocationManager { } public synchronized void register(AlarmManager alarmManager, + CancellationSignal cancellationSignal, ICancellationSignal remoteCancellationSignal) { if (mConsumer == null) { return; @@ -2585,10 +2585,18 @@ public class LocationManager { this, null); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(this); + } + mRemoteCancellationSignal = remoteCancellationSignal; } - public void cancel() { + @Override + public void onCancel() { + synchronized (this) { + mRemoteCancellationSignal = null; + } remove(); } @@ -2637,11 +2645,6 @@ public class LocationManager { @Override public void onLocationChanged(Location location) { - synchronized (this) { - // save ourselves a pointless x-process call to cancel the location request - mRemoteCancellationSignal = null; - } - deliverResult(location); } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 1c0a526f536c..de2a7b2c15e3 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -807,7 +807,8 @@ public class AudioTrack extends PlayerBase int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/, - offload, encapsulationMode, tunerConfiguration); + offload, encapsulationMode, tunerConfiguration, + getCurrentOpPackageName()); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -893,7 +894,8 @@ public class AudioTrack extends PlayerBase nativeTrackInJavaObj, false /*offload*/, ENCAPSULATION_MODE_NONE, - null /* tunerConfiguration */); + null /* tunerConfiguration */, + "" /* opPackagename */); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -4062,7 +4064,8 @@ public class AudioTrack extends PlayerBase Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack, - boolean offload, int encapsulationMode, Object tunerConfiguration); + boolean offload, int encapsulationMode, Object tunerConfiguration, + @NonNull String opPackageName); private native final void native_finalize(); diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index ed566a50ec58..533f6950fb89 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -68,6 +68,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.CRC32; @@ -2079,7 +2080,10 @@ public class ExifInterface { try { // Move the original file to temporary file. if (mFilename != null) { - tempFile = new File(mFilename + ".tmp"); + String parent = originalFile.getParent(); + String name = originalFile.getName(); + String tempPrefix = UUID.randomUUID().toString() + "_"; + tempFile = new File(parent, tempPrefix + name); if (!originalFile.renameTo(tempFile)) { throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath()); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 49e416080041..36ae3ec75a99 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -672,7 +672,8 @@ public class MediaPlayer extends PlayerBase /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ - native_setup(new WeakReference<MediaPlayer>(this)); + native_setup(new WeakReference<MediaPlayer>(this), + getCurrentOpPackageName()); baseRegisterPlayer(); } @@ -2378,7 +2379,7 @@ public class MediaPlayer extends PlayerBase private native final int native_setMetadataFilter(Parcel request); private static native final void native_init(); - private native final void native_setup(Object mediaplayer_this); + private native void native_setup(Object mediaplayerThis, @NonNull String opPackageName); private native final void native_finalize(); /** diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index ee8f1b3eec77..df5e85edbc30 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -27,6 +27,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -622,4 +623,8 @@ public abstract class PlayerBase { Log.w(className, "See the documentation of " + opName + " for what to use instead with " + "android.media.AudioAttributes to qualify your playback use case"); } + + protected String getCurrentOpPackageName() { + return TextUtils.emptyIfNull(ActivityThread.currentOpPackageName()); + } } diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index 3c2be5f93e30..dba86d93a809 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -185,7 +185,7 @@ public final class MediaBrowser { boolean bound = false; try { bound = mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE); + Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES); } catch (Exception ex) { Log.e(TAG, "Failed binding to service " + mServiceComponent); } diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 5cb42a9a96cc..1516c5152054 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -33,6 +33,7 @@ #include <utils/threads.h> #include "jni.h" #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/Log.h" @@ -944,10 +945,12 @@ android_media_MediaPlayer_native_init(JNIEnv *env) } static void -android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) +android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jstring opPackageName) { ALOGV("native_setup"); - sp<MediaPlayer> mp = new MediaPlayer(); + ScopedUtfChars opPackageNameStr(env, opPackageName); + sp<MediaPlayer> mp = new MediaPlayer(opPackageNameStr.c_str()); if (mp == NULL) { jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; @@ -1403,7 +1406,7 @@ static const JNINativeMethod gMethods[] = { {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter}, {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata}, {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, - {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml index 8b235e6f246e..d57875f8daba 100644 --- a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -53,7 +53,7 @@ android:textColor="?attr/wallpaperTextColor" android:textAppearance="?android:attr/textAppearanceMedium" android:imeOptions="flagForceAscii|actionDone" - android:maxLength="@integer/password_text_view_scale" + android:maxLength="@integer/password_max_length" /> <TextView diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 2c9788955bfa..e7295aa6383d 100644 --- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml @@ -25,7 +25,8 @@ <ViewStub android:id="@+id/notification_panel_stub" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout="@layout/notification_panel_container"/> + android:layout="@layout/notification_panel_container" + android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/> <ViewStub android:id="@+id/keyguard_stub" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 039f2c039ded..24157f9e2038 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -53,6 +53,16 @@ <integer name="config_rightSystemBarZOrder">0</integer> <integer name="config_bottomSystemBarZOrder">10</integer> + <!-- If set to true, the corresponding system bar will be hidden when Keyboard (IME) appears. + NOTE: hideBottomSystemBarKeyboard must not be overlaid directly here. To change its value, + overlay config_automotiveHideNavBarForKeyboard in framework/base/core/res/res. --> + <bool name="config_hideTopSystemBarForKeyboard">false</bool> + <bool name="config_hideBottomSystemBarForKeyboard"> + @*android:bool/config_automotiveHideNavBarForKeyboard + </bool> + <bool name="config_hideLeftSystemBarForKeyboard">false</bool> + <bool name="config_hideRightSystemBarForKeyboard">false</bool> + <!-- Disable normal notification rendering; we handle that ourselves --> <bool name="config_renderNotifications">false</bool> diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 8359dac6a30f..e8ac9e14eca1 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -194,6 +194,12 @@ <dimen name="car_navigation_bar_width">760dp</dimen> <dimen name="car_left_navigation_bar_width">96dp</dimen> <dimen name="car_right_navigation_bar_width">96dp</dimen> + <!-- In order to change the height of the bottom nav bar, overlay navigation_bar_height in + frameworks/base/core/res/res instead. --> + <dimen name="car_bottom_navigation_bar_height">@*android:dimen/navigation_bar_height</dimen> + <!-- In order to change the height of the top nav bar, overlay status_bar_height in + frameworks/base/core/res/res instead. --> + <dimen name="car_top_navigation_bar_height">@*android:dimen/status_bar_height</dimen> <dimen name="car_user_switcher_container_height">420dp</dimen> <!-- This must be the negative of car_user_switcher_container_height for the animation. --> diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index 51a7245ea5c6..ec018f9bb62e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -21,10 +21,13 @@ import android.car.user.CarUserManager; import android.content.Context; import android.os.Bundle; import android.os.Handler; +import android.os.UserHandle; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import androidx.annotation.VisibleForTesting; @@ -61,7 +64,7 @@ import dagger.Lazy; public class CarKeyguardViewController extends OverlayViewController implements KeyguardViewController { private static final String TAG = "CarKeyguardViewController"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private final Context mContext; private final Handler mHandler; @@ -75,9 +78,10 @@ public class CarKeyguardViewController extends OverlayViewController implements private final DismissCallbackRegistry mDismissCallbackRegistry; private final ViewMediatorCallback mViewMediatorCallback; private final CarNavigationBarController mCarNavigationBarController; + private final InputMethodManager mInputMethodManager; // Needed to instantiate mBouncer. - private final KeyguardBouncer.BouncerExpansionCallback - mExpansionCallback = new KeyguardBouncer.BouncerExpansionCallback() { + private final KeyguardBouncer.BouncerExpansionCallback mExpansionCallback = + new KeyguardBouncer.BouncerExpansionCallback() { @Override public void onFullyShown() { } @@ -96,7 +100,8 @@ public class CarKeyguardViewController extends OverlayViewController implements }; private final CarUserManager.UserLifecycleListener mUserLifecycleListener = (e) -> { if (e.getEventType() == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) { - revealKeyguardIfBouncerPrepared(); + UserHandle currentUser = e.getUserHandle(); + revealKeyguardIfBouncerPrepared(currentUser); } }; @@ -136,12 +141,14 @@ public class CarKeyguardViewController extends OverlayViewController implements mDismissCallbackRegistry = dismissCallbackRegistry; mViewMediatorCallback = viewMediatorCallback; mCarNavigationBarController = carNavigationBarController; + // TODO(b/169280588): Inject InputMethodManager instead. + mInputMethodManager = mContext.getSystemService(InputMethodManager.class); registerUserSwitchedListener(); } @Override - protected boolean shouldShowNavigationBar() { + protected boolean shouldShowNavigationBarInsets() { return true; } @@ -177,7 +184,6 @@ public class CarKeyguardViewController extends OverlayViewController implements mKeyguardStateController.notifyKeyguardState(mShowing, /* occluded= */ false); mCarNavigationBarController.showAllKeyguardButtons(/* isSetUp= */ true); start(); - getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ true); reset(/* hideBouncerWhenShowing= */ false); notifyKeyguardUpdateMonitor(); } @@ -192,7 +198,6 @@ public class CarKeyguardViewController extends OverlayViewController implements mBouncer.hide(/* destroyView= */ true); mCarNavigationBarController.hideAllKeyguardButtons(/* isSetUp= */ true); stop(); - getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false); mKeyguardStateController.notifyKeyguardDoneFading(); mHandler.post(mViewMediatorCallback::keyguardGone); notifyKeyguardUpdateMonitor(); @@ -237,7 +242,6 @@ public class CarKeyguardViewController extends OverlayViewController implements public void onCancelClicked() { if (mBouncer == null) return; - getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false); getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false); mBouncer.hide(/* destroyView= */ true); @@ -366,9 +370,9 @@ public class CarKeyguardViewController extends OverlayViewController implements } /** - * Hides Keyguard so that the transitioning Bouncer can be hidden until it is prepared. To be - * called by {@link com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator} - * when a new user is selected. + * Hides Keyguard so that the transitioning Bouncer can be hidden until it is prepared. To be + * called by {@link com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator} + * when a new user is selected. */ public void hideKeyguardToPrepareBouncer() { getLayout().setVisibility(View.INVISIBLE); @@ -379,29 +383,41 @@ public class CarKeyguardViewController extends OverlayViewController implements mBouncer = keyguardBouncer; } - private void revealKeyguardIfBouncerPrepared() { + private void revealKeyguardIfBouncerPrepared(UserHandle currentUser) { int reattemptDelayMillis = 50; Runnable revealKeyguard = () -> { if (mBouncer == null) { if (DEBUG) { Log.d(TAG, "revealKeyguardIfBouncerPrepared: revealKeyguard request is ignored " - + "since the Bouncer has not been initialized yet."); + + "since the Bouncer has not been initialized yet."); } return; } if (!mBouncer.inTransit() || !mBouncer.isSecure()) { getLayout().setVisibility(View.VISIBLE); + updateCurrentUserForPasswordEntry(currentUser); } else { if (DEBUG) { Log.d(TAG, "revealKeyguardIfBouncerPrepared: Bouncer is not prepared " + "yet so reattempting after " + reattemptDelayMillis + "ms."); } - mHandler.postDelayed(this::revealKeyguardIfBouncerPrepared, reattemptDelayMillis); + mHandler.postDelayed(() -> revealKeyguardIfBouncerPrepared(currentUser), + reattemptDelayMillis); } }; mHandler.post(revealKeyguard); } + private void updateCurrentUserForPasswordEntry(UserHandle currentUser) { + EditText passwordEntry = getLayout().findViewById(R.id.passwordEntry); + if (passwordEntry != null) { + mHandler.post(() -> { + mInputMethodManager.restartInput(passwordEntry); + passwordEntry.setTextOperationUser(currentUser); + }); + } + } + private void notifyKeyguardUpdateMonitor() { mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(mShowing); if (mBouncer != null) { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java index 9584850fde7c..b6d251fbfe16 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java @@ -91,7 +91,11 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks private ActivityManagerWrapper mActivityManagerWrapper; // If the nav bar should be hidden when the soft keyboard is visible. - private boolean mHideNavBarForKeyboard; + private boolean mHideTopBarForKeyboard; + private boolean mHideLeftBarForKeyboard; + private boolean mHideRightBarForKeyboard; + private boolean mHideBottomBarForKeyboard; + private boolean mBottomNavBarVisible; // Nav bar views. @@ -160,8 +164,13 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks @Override public void start() { // Set initial state. - mHideNavBarForKeyboard = mResources.getBoolean( - com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); + mHideTopBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP); + mHideBottomBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide( + SystemBarConfigs.BOTTOM); + mHideLeftBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.LEFT); + mHideRightBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide( + SystemBarConfigs.RIGHT); + mBottomNavBarVisible = false; // Connect into the status bar manager service @@ -407,17 +416,30 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { - if (!mHideNavBarForKeyboard) { - return; - } - if (mContext.getDisplayId() != displayId) { return; } boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; - mCarNavigationBarController.setBottomWindowVisibility( - isKeyboardVisible ? View.GONE : View.VISIBLE); + + if (mHideTopBarForKeyboard) { + mCarNavigationBarController.setTopWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } + + if (mHideBottomBarForKeyboard) { + mCarNavigationBarController.setBottomWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } + + if (mHideLeftBarForKeyboard) { + mCarNavigationBarController.setLeftWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } + if (mHideRightBarForKeyboard) { + mCarNavigationBarController.setRightWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java index fe26040c5eae..e522d19249e3 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java @@ -79,9 +79,7 @@ public class CarNavigationBarController { * Hides all system bars. */ public void hideBars() { - if (mTopView != null) { - mTopView.setVisibility(View.GONE); - } + setTopWindowVisibility(View.GONE); setBottomWindowVisibility(View.GONE); setLeftWindowVisibility(View.GONE); setRightWindowVisibility(View.GONE); @@ -91,9 +89,7 @@ public class CarNavigationBarController { * Shows all system bars. */ public void showBars() { - if (mTopView != null) { - mTopView.setVisibility(View.VISIBLE); - } + setTopWindowVisibility(View.VISIBLE); setBottomWindowVisibility(View.VISIBLE); setLeftWindowVisibility(View.VISIBLE); setRightWindowVisibility(View.VISIBLE); @@ -135,6 +131,11 @@ public class CarNavigationBarController { return mShowRight ? mNavigationBarViewFactory.getRightWindow() : null; } + /** Toggles the top nav bar visibility. */ + public boolean setTopWindowVisibility(@View.Visibility int visibility) { + return setWindowVisibility(getTopWindow(), visibility); + } + /** Toggles the bottom nav bar visibility. */ public boolean setBottomWindowVisibility(@View.Visibility int visibility) { return setWindowVisibility(getBottomWindow(), visibility); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java index 3527bf93682f..2efa2b3d4870 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java @@ -29,6 +29,8 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import com.android.systemui.car.notification.BottomNotificationPanelViewMediator; +import com.android.systemui.car.notification.TopNotificationPanelViewMediator; import com.android.systemui.dagger.qualifiers.Main; import java.lang.annotation.ElementType; @@ -94,7 +96,11 @@ public class SystemBarConfigs { populateMaps(); readConfigs(); + checkEnabledBarsHaveUniqueBarTypes(); + checkSystemBarEnabledForNotificationPanel(); + checkHideBottomBarForKeyboardConfigSync(); + setInsetPaddingsForOverlappingCorners(); sortSystemBarSidesByZOrder(); } @@ -119,6 +125,11 @@ public class SystemBarConfigs { } } + protected boolean getHideForKeyboardBySide(@SystemBarSide int side) { + return mSystemBarConfigMap.get(side) != null + && mSystemBarConfigMap.get(side).getHideForKeyboard(); + } + protected void insetSystemBar(@SystemBarSide int side, CarNavigationBarView view) { int[] paddings = mSystemBarConfigMap.get(side).getPaddings(); view.setPadding(paddings[2], paddings[0], paddings[3], paddings[1]); @@ -144,10 +155,10 @@ public class SystemBarConfigs { BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar"); BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar"); - BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_GESTURES); - BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_GESTURES); - BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_GESTURES); - BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_GESTURES); + BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_MANDATORY_GESTURES); + BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES); + BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_MANDATORY_GESTURES); + BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_MANDATORY_GESTURES); } private void readConfigs() { @@ -161,9 +172,11 @@ public class SystemBarConfigs { new SystemBarConfigBuilder() .setSide(TOP) .setGirth(mResources.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height)) + R.dimen.car_top_navigation_bar_height)) .setBarType(mResources.getInteger(R.integer.config_topSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideTopSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(TOP, topBarConfig); } @@ -173,10 +186,12 @@ public class SystemBarConfigs { new SystemBarConfigBuilder() .setSide(BOTTOM) .setGirth(mResources.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height)) + R.dimen.car_bottom_navigation_bar_height)) .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType)) .setZOrder( mResources.getInteger(R.integer.config_bottomSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideBottomSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(BOTTOM, bottomBarConfig); } @@ -189,6 +204,8 @@ public class SystemBarConfigs { R.dimen.car_left_navigation_bar_width)) .setBarType(mResources.getInteger(R.integer.config_leftSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_leftSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideLeftSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(LEFT, leftBarConfig); } @@ -201,6 +218,8 @@ public class SystemBarConfigs { R.dimen.car_right_navigation_bar_width)) .setBarType(mResources.getInteger(R.integer.config_rightSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_rightSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideRightSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(RIGHT, rightBarConfig); } @@ -221,6 +240,52 @@ public class SystemBarConfigs { } } + private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException { + + String notificationPanelMediatorName = + mResources.getString(R.string.config_notificationPanelViewMediator); + if (notificationPanelMediatorName == null) { + return; + } + + Class<?> notificationPanelMediatorUsed = null; + try { + notificationPanelMediatorUsed = Class.forName(notificationPanelMediatorName); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom( + notificationPanelMediatorUsed)) { + throw new RuntimeException( + "Top System Bar must be enabled to use " + notificationPanelMediatorName); + } + + if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom( + notificationPanelMediatorUsed)) { + throw new RuntimeException("Bottom System Bar must be enabled to use " + + notificationPanelMediatorName); + } + } + + private void checkHideBottomBarForKeyboardConfigSync() throws RuntimeException { + if (mBottomNavBarEnabled) { + boolean actual = mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard); + boolean expected = mResources.getBoolean( + com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); + + if (actual != expected) { + throw new RuntimeException("config_hideBottomSystemBarForKeyboard must not be " + + "overlaid directly and should always refer to" + + "config_automotiveHideNavBarForKeyboard. However, their values " + + "currently do not sync. Set config_hideBottomSystemBarForKeyguard to " + + "@*android:bool/config_automotiveHideNavBarForKeyboard. To change its " + + "value, overlay config_automotiveHideNavBarForKeyboard in " + + "framework/base/core/res/res."); + } + } + } + private void setInsetPaddingsForOverlappingCorners() { setInsetPaddingForOverlappingCorner(TOP, LEFT); setInsetPaddingForOverlappingCorner(TOP, RIGHT); @@ -277,7 +342,7 @@ public class SystemBarConfigs { } private static boolean isHorizontalBar(@SystemBarSide int side) { - return side == TOP || side == BOTTOM; + return side == TOP || side == BOTTOM; } private static boolean isVerticalBar(@SystemBarSide int side) { @@ -289,14 +354,17 @@ public class SystemBarConfigs { private final int mBarType; private final int mGirth; private final int mZOrder; + private final boolean mHideForKeyboard; private int[] mPaddings = new int[]{0, 0, 0, 0}; - private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder) { + private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder, + boolean hideForKeyboard) { mSide = side; mBarType = barType; mGirth = girth; mZOrder = zOrder; + mHideForKeyboard = hideForKeyboard; } private int getSide() { @@ -315,6 +383,10 @@ public class SystemBarConfigs { return mZOrder; } + private boolean getHideForKeyboard() { + return mHideForKeyboard; + } + private int[] getPaddings() { return mPaddings; } @@ -352,6 +424,7 @@ public class SystemBarConfigs { private int mBarType; private int mGirth; private int mZOrder; + private boolean mHideForKeyboard; private SystemBarConfigBuilder setSide(@SystemBarSide int side) { mSide = side; @@ -373,8 +446,13 @@ public class SystemBarConfigs { return this; } + private SystemBarConfigBuilder setHideForKeyboard(boolean hide) { + mHideForKeyboard = hide; + return this; + } + private SystemBarConfig build() { - return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder); + return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard); } } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index a7cb0d86a6a8..fd804c71c9d0 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -16,8 +16,6 @@ package com.android.systemui.car.notification; -import static android.view.WindowInsets.Type.navigationBars; - import android.app.ActivityManager; import android.car.Car; import android.car.drivingstate.CarUxRestrictionsManager; @@ -25,6 +23,8 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.inputmethodservice.InputMethodService; +import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.GestureDetector; @@ -82,6 +82,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController private final StatusBarStateController mStatusBarStateController; private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen; private final NotificationVisibilityLogger mNotificationVisibilityLogger; + private final int mNavBarHeight; private float mInitialBackgroundAlpha; private float mBackgroundAlphaDiff; @@ -138,7 +139,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController mStatusBarStateController = statusBarStateController; mNotificationVisibilityLogger = notificationVisibilityLogger; + mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height); + mCommandQueue.addCallback(this); + // Notification background setup. mInitialBackgroundAlpha = (float) mResources.getInteger( R.integer.config_initialNotificationBackgroundAlpha) / 100; @@ -179,6 +183,27 @@ public class NotificationPanelViewController extends OverlayPanelViewController } } + @Override + public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { + if (mContext.getDisplayId() != displayId) { + return; + } + boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; + int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight; + ViewGroup container = (ViewGroup) getLayout(); + if (container == null) { + // Notification panel hasn't been inflated before. We shouldn't try to update the layout + // params. + return; + } + + ViewGroup.MarginLayoutParams params = + (ViewGroup.MarginLayoutParams) container.getLayoutParams(); + params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin); + container.setLayoutParams(params); + } + // OverlayViewController @Override @@ -193,18 +218,18 @@ public class NotificationPanelViewController extends OverlayPanelViewController } @Override - protected boolean shouldShowNavigationBar() { + protected boolean shouldShowNavigationBarInsets() { return true; } @Override - protected boolean shouldShowStatusBar() { + protected boolean shouldShowStatusBarInsets() { return true; } @Override protected int getInsetTypesToFit() { - return navigationBars(); + return 0; } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java index 1a8f19e46798..aac4cfbf83c4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java @@ -78,6 +78,11 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController } @Override + protected boolean shouldFocusWindow() { + return false; + } + + @Override protected void showInternal() { getLayout().setVisibility(View.VISIBLE); } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java index 1b00c6301011..3c9879c671a5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java @@ -238,7 +238,6 @@ public abstract class OverlayPanelViewController extends OverlayViewController { } onAnimateCollapsePanel(); - getOverlayViewGlobalStateController().setWindowFocusable(false); animatePanel(mClosingVelocity, /* isClosing= */ true); } @@ -415,7 +414,6 @@ public abstract class OverlayPanelViewController extends OverlayViewController { getOverlayViewGlobalStateController().hideView(/* panelViewController= */ this); } getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - getOverlayViewGlobalStateController().setWindowFocusable(visible); } /* ***************************************************************************************** * diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java index 53deb9d9dc5d..8adc1adcc41c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java @@ -136,16 +136,18 @@ public class OverlayViewController { } /** - * Returns {@code true} if navigation bar should be displayed over this view. + * Returns {@code true} if navigation bar insets should be displayed over this view. Has no + * effect if {@link #shouldFocusWindow} returns {@code false}. */ - protected boolean shouldShowNavigationBar() { + protected boolean shouldShowNavigationBarInsets() { return false; } /** - * Returns {@code true} if status bar should be displayed over this view. + * Returns {@code true} if status bar insets should be displayed over this view. Has no + * effect if {@link #shouldFocusWindow} returns {@code false}. */ - protected boolean shouldShowStatusBar() { + protected boolean shouldShowStatusBarInsets() { return false; } @@ -157,6 +159,15 @@ public class OverlayViewController { } /** + * Returns {@code true} if the window should be focued when this view is visible. Note that + * returning {@code false} here means that {@link #shouldShowStatusBarInsets} and + * {@link #shouldShowNavigationBarInsets} will have no effect. + */ + protected boolean shouldFocusWindow() { + return true; + } + + /** * Returns the insets types to fit to the sysui overlay window when this * {@link OverlayViewController} is in the foreground. */ diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java index 2494242c24f0..55f0975aeccf 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java @@ -18,7 +18,6 @@ package com.android.systemui.car.window; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import android.annotation.Nullable; import android.util.Log; @@ -118,6 +117,7 @@ public class OverlayViewGlobalStateController { updateInternalsWhenShowingView(viewController); refreshInsetTypesToFit(); + refreshWindowFocus(); refreshNavigationBarVisibility(); refreshStatusBarVisibility(); @@ -190,6 +190,7 @@ public class OverlayViewGlobalStateController { mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController)); refreshHighestZOrderWhenHidingView(viewController); refreshInsetTypesToFit(); + refreshWindowFocus(); refreshNavigationBarVisibility(); refreshStatusBarVisibility(); @@ -214,23 +215,37 @@ public class OverlayViewGlobalStateController { } private void refreshNavigationBarVisibility() { - mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); - if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) { + if (mZOrderVisibleSortedMap.isEmpty()) { mWindowInsetsController.show(navigationBars()); - } else { + return; + } + + // Do not hide navigation bar insets if the window is not focusable. + if (mHighestZOrder.shouldFocusWindow() && !mHighestZOrder.shouldShowNavigationBarInsets()) { mWindowInsetsController.hide(navigationBars()); + } else { + mWindowInsetsController.show(navigationBars()); } } private void refreshStatusBarVisibility() { - mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); - if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) { + if (mZOrderVisibleSortedMap.isEmpty()) { mWindowInsetsController.show(statusBars()); - } else { + return; + } + + // Do not hide status bar insets if the window is not focusable. + if (mHighestZOrder.shouldFocusWindow() && !mHighestZOrder.shouldShowStatusBarInsets()) { mWindowInsetsController.hide(statusBars()); + } else { + mWindowInsetsController.show(statusBars()); } } + private void refreshWindowFocus() { + setWindowFocusable(mHighestZOrder == null ? false : mHighestZOrder.shouldFocusWindow()); + } + private void refreshInsetTypesToFit() { if (mZOrderVisibleSortedMap.isEmpty()) { setFitInsetsTypes(statusBars()); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java index 029bd3702afe..c955fab592f3 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java @@ -16,6 +16,7 @@ package com.android.systemui.car.window; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import android.content.Context; @@ -104,6 +105,7 @@ public class SystemUIOverlayWindowController implements mLp.setTitle("SystemUIOverlayWindow"); mLp.packageName = mContext.getPackageName(); mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; mWindowManager.addView(mBaseLayout, mLp); mLpChanged.copyFrom(mLp); @@ -160,6 +162,7 @@ public class SystemUIOverlayWindowController implements private void updateWindow() { if (mLp != null && mLp.copyFrom(mLpChanged) != 0) { if (isAttached()) { + mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; mWindowManager.updateViewLayout(mBaseLayout, mLp); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java index a831464e7987..c9ec34fd5f08 100644 --- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java +++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java @@ -32,6 +32,8 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.TransactionPool; import com.android.systemui.dagger.qualifiers.Main; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Singleton; @@ -90,7 +92,7 @@ public class DisplaySystemBarsController extends DisplayImeController { } @VisibleForTesting - class PerDisplay extends IDisplayWindowInsetsController.Stub { + class PerDisplay extends DisplayImeController.PerDisplay { int mDisplayId; InsetsController mInsetsController; @@ -98,6 +100,8 @@ public class DisplaySystemBarsController extends DisplayImeController { String mPackageName; PerDisplay(int displayId) { + super(displayId, + mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()); mDisplayId = displayId; mInsetsController = new InsetsController( new DisplaySystemBarsInsetsControllerHost(mHandler, this)); @@ -105,6 +109,7 @@ public class DisplaySystemBarsController extends DisplayImeController { @Override public void insetsChanged(InsetsState insetsState) { + super.insetsChanged(insetsState); if (mInsetsState.equals(insetsState)) { return; } @@ -118,24 +123,33 @@ public class DisplaySystemBarsController extends DisplayImeController { @Override public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { + super.insetsControlChanged(insetsState, activeControls); mInsetsController.onControlsChanged(activeControls); } @Override public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { - mInsetsController.hide(types); + if ((types & WindowInsets.Type.ime()) == 0) { + mInsetsController.hide(types); + } else { + super.hideInsets(types, fromIme); + } + } @Override public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { - mInsetsController.show(types); + if ((types & WindowInsets.Type.ime()) == 0) { + mInsetsController.show(types); + } else { + super.showInsets(types, fromIme); + } + } @Override public void topFocusedWindowChanged(String packageName) { - // If both package names are null or both package names are equal, return. - if (mPackageName == packageName - || (mPackageName != null && mPackageName.equals(packageName))) { + if (Objects.equals(mPackageName, packageName)) { return; } mPackageName = packageName; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java index 0b164a2e1a51..3fd0852bc0ff 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java @@ -48,6 +48,10 @@ import org.mockito.MockitoAnnotations; @SmallTest public class CarNavigationBarControllerTest extends SysuiTestCase { + private static final String TOP_NOTIFICATION_PANEL = + "com.android.systemui.car.notification.TopNotificationPanelViewMediator"; + private static final String BOTTOM_NOTIFICATION_PANEL = + "com.android.systemui.car.notification.BottomNotificationPanelViewMediator"; private CarNavigationBarController mCarNavigationBar; private NavigationBarViewFactory mNavigationBarViewFactory; private TestableResources mTestableResources; @@ -117,6 +121,11 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { @Test public void testGetTopWindow_topDisabled_returnsNull() { mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, false); + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + // If Top Notification Panel is used but top navigation bar is not enabled, SystemUI is + // expected to crash. + mTestableResources.addOverride(R.string.config_notificationPanelViewMediator, + BOTTOM_NOTIFICATION_PANEL); mCarNavigationBar = createNavigationBarController(); ViewGroup window = mCarNavigationBar.getTopWindow(); @@ -148,6 +157,11 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { @Test public void testGetBottomWindow_bottomDisabled_returnsNull() { mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false); + mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true); + // If Bottom Notification Panel is used but bottom navigation bar is not enabled, + // SystemUI is expected to crash. + mTestableResources.addOverride(R.string.config_notificationPanelViewMediator, + TOP_NOTIFICATION_PANEL); mCarNavigationBar = createNavigationBarController(); ViewGroup window = mCarNavigationBar.getBottomWindow(); @@ -237,6 +251,28 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { } @Test + public void testSetTopWindowVisibility_setTrue_isVisible() { + mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true); + mCarNavigationBar = createNavigationBarController(); + + ViewGroup window = mCarNavigationBar.getTopWindow(); + mCarNavigationBar.setTopWindowVisibility(View.VISIBLE); + + assertThat(window.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testSetTopWindowVisibility_setFalse_isGone() { + mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true); + mCarNavigationBar = createNavigationBarController(); + + ViewGroup window = mCarNavigationBar.getTopWindow(); + mCarNavigationBar.setTopWindowVisibility(View.GONE); + + assertThat(window.getVisibility()).isEqualTo(View.GONE); + } + + @Test public void testSetBottomWindowVisibility_setTrue_isVisible() { mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); mCarNavigationBar = createNavigationBarController(); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java index 8b1589913d1d..96f05045cd38 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java @@ -17,6 +17,7 @@ package com.android.systemui.car.navigationbar; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -31,7 +32,14 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarSystemUiTest; +import com.android.systemui.car.notification.NotificationPanelViewController; +import com.android.systemui.car.notification.NotificationPanelViewMediator; +import com.android.systemui.car.notification.PowerManagerHelper; +import com.android.systemui.car.notification.TopNotificationPanelViewMediator; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; @@ -92,6 +100,43 @@ public class SystemBarConfigsTest extends SysuiTestCase { mSystemBarConfigs = new SystemBarConfigs(mResources); } + @Test(expected = RuntimeException.class) + public void onInit_hideBottomSystemBarForKeyboardValueDoNotSync_throwsRuntimeException() { + when(mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard)).thenReturn(false); + when(mResources.getBoolean( + com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard)).thenReturn( + true); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + + @Test + public void onInit_topNotifPanelViewMediatorUsed_topBarEnabled_doesNotThrowException() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(true); + when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn( + TestTopNotificationPanelViewMediator.class.getName()); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + + @Test(expected = RuntimeException.class) + public void onInit_topNotifPanelViewMediatorUsed_topBarNotEnabled_throwsRuntimeException() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false); + when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn( + TestTopNotificationPanelViewMediator.class.getName()); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + + @Test + public void onInit_notificationPanelViewMediatorUsed_topBarNotEnabled_doesNotThrowException() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false); + when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn( + NotificationPanelViewMediator.class.getName()); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + @Test public void getTopSystemBarLayoutParams_topBarEnabled_returnsTopSystemBarLayoutParams() { mSystemBarConfigs = new SystemBarConfigs(mResources); @@ -112,6 +157,26 @@ public class SystemBarConfigsTest extends SysuiTestCase { } @Test + public void getTopSystemBarHideForKeyboard_hideBarForKeyboard_returnsTrue() { + when(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).thenReturn(true); + mSystemBarConfigs = new SystemBarConfigs(mResources); + + boolean hideKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP); + + assertTrue(hideKeyboard); + } + + @Test + public void getTopSystemBarHideForKeyboard_topBarNotEnabled_returnsFalse() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false); + mSystemBarConfigs = new SystemBarConfigs(mResources); + + boolean hideKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP); + + assertFalse(hideKeyboard); + } + + @Test public void topSystemBarHasHigherZOrderThanHuns_topSystemBarIsNavigationBarPanelType() { when(mResources.getInteger(R.integer.config_topSystemBarZOrder)).thenReturn( SystemBarConfigs.getHunZOrder() + 1); @@ -158,5 +223,30 @@ public class SystemBarConfigsTest extends SysuiTestCase { when(mResources.getInteger(R.integer.config_bottomSystemBarZOrder)).thenReturn(10); when(mResources.getInteger(R.integer.config_leftSystemBarZOrder)).thenReturn(2); when(mResources.getInteger(R.integer.config_rightSystemBarZOrder)).thenReturn(3); + + when(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).thenReturn(false); + when(mResources.getBoolean( + com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard)).thenReturn( + false); + when(mResources.getBoolean(R.bool.config_hideLeftSystemBarForKeyboard)).thenReturn( + false); + when(mResources.getBoolean(R.bool.config_hideRightSystemBarForKeyboard)).thenReturn( + false); + } + + // Intentionally using a subclass of TopNotificationPanelViewMediator for testing purposes to + // ensure that OEM's will be able to implement and use their own NotificationPanelViewMediator. + private class TestTopNotificationPanelViewMediator extends + TopNotificationPanelViewMediator { + TestTopNotificationPanelViewMediator( + CarNavigationBarController carNavigationBarController, + NotificationPanelViewController notificationPanelViewController, + PowerManagerHelper powerManagerHelper, + BroadcastDispatcher broadcastDispatcher, + CarDeviceProvisionedController carDeviceProvisionedController, + ConfigurationController configurationController) { + super(carNavigationBarController, notificationPanelViewController, powerManagerHelper, + broadcastDispatcher, carDeviceProvisionedController, configurationController); + } } } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java index 7311cdb68a3c..23e21e4cbed6 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java @@ -224,18 +224,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase { } @Test - public void animateCollapsePanel_removesWindowFocus() { - mOverlayPanelViewController.inflate(mBaseLayout); - mOverlayPanelViewController.setShouldAnimateCollapsePanel(true); - mOverlayPanelViewController.setPanelExpanded(true); - mOverlayPanelViewController.setPanelVisible(true); - - mOverlayPanelViewController.animateCollapsePanel(); - - verify(mOverlayViewGlobalStateController).setWindowFocusable(false); - } - - @Test public void animateExpandPanel_shouldNotAnimateExpandPanel_doesNotExpand() { mOverlayPanelViewController.inflate(mBaseLayout); mOverlayPanelViewController.setShouldAnimateExpandPanel(false); @@ -365,14 +353,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase { } @Test - public void setPanelVisible_setTrue_setWindowFocusable() { - mOverlayPanelViewController.inflate(mBaseLayout); - mOverlayPanelViewController.setPanelVisible(true); - - verify(mOverlayViewGlobalStateController).setWindowFocusable(true); - } - - @Test public void setPanelVisible_setFalse_windowVisible_setsWindowNotVisible() { mOverlayPanelViewController.inflate(mBaseLayout); when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true); @@ -404,15 +384,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase { } @Test - public void setPanelVisible_setFalse_setWindowNotFocusable() { - mOverlayPanelViewController.inflate(mBaseLayout); - - mOverlayPanelViewController.setPanelVisible(false); - - verify(mOverlayViewGlobalStateController).setWindowFocusable(false); - } - - @Test public void dragOpenTouchListener_isNotInflated_inflatesView() { when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true); assertThat(mOverlayPanelViewController.isInflated()).isFalse(); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java index ff286650ea50..294aa0d3cf9b 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java @@ -108,9 +108,54 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { } @Test + public void showView_nothingVisible_windowNotFocusable_shouldShowNavBar_navBarsVisible() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController).show(navigationBars()); + } + + @Test + public void showView_nothingVisible_windowNotFocusable_shouldHideNavBar_notHidden() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController, never()).hide(navigationBars()); + } + + @Test + public void showView_nothingVisible_windowNotFocusable_shouldShowStatusBar_statusBarsVisible() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController).show(statusBars()); + } + + @Test + public void showView_nothingVisible_windowNotFocusable_shouldHideStatusBar_notHidden() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController, never()).hide(statusBars()); + } + + @Test public void showView_nothingAlreadyShown_shouldShowNavBarFalse_navigationBarsHidden() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -120,7 +165,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_nothingAlreadyShown_shouldShowNavBarTrue_navigationBarsShown() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -130,7 +176,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -140,7 +187,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -201,9 +249,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -214,9 +264,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -226,9 +278,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -239,9 +293,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -289,9 +345,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -302,9 +360,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -314,9 +374,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -327,9 +389,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -512,10 +576,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -526,10 +592,13 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowNavBarTrue_navigationBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); + reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -539,10 +608,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -553,10 +624,13 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); + reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -593,10 +667,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -607,10 +683,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -620,10 +698,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -634,10 +714,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -673,6 +755,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_viewControllerOnlyShown_navigationBarShown() { setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController1); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -683,6 +766,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_viewControllerOnlyShown_statusBarShown() { setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController1); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); diff --git a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml index 47eeb839c6dc..2b9fa414dcc5 100644 --- a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="keyguard_description" msgid="8582605799129954556">"Geef je wachtwoord op en ga door naar \'Dynamische systeemupdates\'"</string> - <string name="notification_install_completed" msgid="6252047868415172643">"Dynamisch systeem is gereed. Start je apparaat opnieuw op om het te gebruiken."</string> + <string name="notification_install_completed" msgid="6252047868415172643">"Dynamisch systeem is klaar. Start je apparaat opnieuw op om het te gebruiken."</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"Installatie wordt uitgevoerd"</string> <string name="notification_install_failed" msgid="4066039210317521404">"Installatie mislukt"</string> <string name="notification_image_validation_failed" msgid="2720357826403917016">"Valideren van afbeelding mislukt. Installatie afbreken."</string> diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml index 49bf0b7f3aad..423243716208 100644 --- a/packages/InputDevices/res/values-ru/strings.xml +++ b/packages/InputDevices/res/values-ru/strings.xml @@ -42,6 +42,6 @@ <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"испанский (Латинская Америка)"</string> <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string> <string name="keyboard_layout_persian" msgid="3920643161015888527">"Персидский"</string> - <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанский"</string> + <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджанский"</string> <string name="keyboard_layout_polish" msgid="1121588624094925325">"польский"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml index 87f89ce192c4..c840374d3f6b 100644 --- a/packages/PackageInstaller/res/values-ar/strings.xml +++ b/packages/PackageInstaller/res/values-ar/strings.xml @@ -57,7 +57,7 @@ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string> <string name="uninstall_update_text" msgid="863648314632448705">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string> - <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string> + <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد إعادة ضبط هذا التطبيق على الإعدادات الأصلية؟ سؤدي ذلك إلى إزالة جميع البيانات، كما سيؤثر على جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string> <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بالحجم <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"عمليات إلغاء التثبيت الجارية"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"عمليات إلغاء التثبيت غير الناجحة"</string> diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml index 108c86fe8369..d3a958922d1c 100644 --- a/packages/PackageInstaller/res/values-nl/strings.xml +++ b/packages/PackageInstaller/res/values-nl/strings.xml @@ -18,7 +18,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="7488448184431507488">"Pakket-installatie"</string> <string name="install" msgid="711829760615509273">"Installeren"</string> - <string name="done" msgid="6632441120016885253">"Gereed"</string> + <string name="done" msgid="6632441120016885253">"Klaar"</string> <string name="cancel" msgid="1018267193425558088">"Annuleren"</string> <string name="installing" msgid="4921993079741206516">"Installeren…"</string> <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeren…"</string> diff --git a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml index ea66717b1e87..82c8613b5c8c 100644 --- a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml +++ b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="help_feedback_label" msgid="7106780063063027882">"సహాయం & అభిప్రాయం"</string> + <string name="help_feedback_label" msgid="7106780063063027882">"సహాయం & ఫీడ్బ్యాక్"</string> </resources> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 9acfa0da7747..0b4538097e41 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -25,7 +25,7 @@ <string name="wifi_remembered" msgid="3266709779723179188">"تم الحفظ"</string> <string name="wifi_disconnected" msgid="7054450256284661757">"غير متصلة"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"غير مفعّلة"</string> - <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"تعذّرت تهيئة عنوان IP"</string> + <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"تعذّر إعداد عنوان IP"</string> <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string> <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"تعذّر اتصال WiFi"</string> <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"حدثت مشكلة في المصادقة"</string> @@ -153,7 +153,7 @@ <string name="unknown" msgid="3544487229740637809">"غير معروف"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string> - <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string> + <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم ضبط إعدادات تلقائية"</string> <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string> <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"معدل سرعة الكلام"</string> @@ -245,7 +245,7 @@ <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"هل تريد السماح بإلغاء قفل المصنّع الأصلي للجهاز؟"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"تحذير: لن تعمل ميزات الحماية على هذا الجهاز أثناء تفعيل هذا الإعداد."</string> <string name="mock_location_app" msgid="6269380172542248304">"اختيار تطبيق الموقع الزائف"</string> - <string name="mock_location_app_not_set" msgid="6972032787262831155">"لم يتم تعيين تطبيق موقع زائف"</string> + <string name="mock_location_app_not_set" msgid="6972032787262831155">"لم يتم ضبط تطبيق موقع زائف"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"تطبيق الموقع الزائف: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"الشبكات"</string> <string name="wifi_display_certification" msgid="1805579519992520381">"شهادة عرض شاشة لاسلكي"</string> @@ -292,8 +292,8 @@ <string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"عندما نتوقف عن رصد أي أخطاء باستخدام المسجِّل الدائم مرة أخرى، يتعين علينا محو بيانات المسجِّل الموجودة على جهازك."</string> <string name="select_logpersist_title" msgid="447071974007104196">"تخزين بيانات المسجِّل باستمرار على الجهاز"</string> <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"تحديد مخازن السجلات المؤقتة المراد تخزينها باستمرار على الجهاز"</string> - <string name="select_usb_configuration_title" msgid="6339801314922294586">"حدد تهيئة USB"</string> - <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"حدد تهيئة USB"</string> + <string name="select_usb_configuration_title" msgid="6339801314922294586">"حدد إعداد USB"</string> + <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"حدد إعداد USB"</string> <string name="allow_mock_location" msgid="2102650981552527884">"السماح بمواقع وهمية"</string> <string name="allow_mock_location_summary" msgid="179780881081354579">"السماح بمواقع وهمية"</string> <string name="debug_view_attributes" msgid="3539609843984208216">"تفعيل فحص سمة العرض"</string> @@ -318,7 +318,7 @@ <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"تعيين سلوك التحقق من HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"تصحيح الأخطاء"</string> <string name="debug_app" msgid="8903350241392391766">"اختيار التطبيق لتصحيحه"</string> - <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم تعيين تطبيق لتصحيحه"</string> + <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم ضبط تطبيق لتصحيحه"</string> <string name="debug_app_set" msgid="6599535090477753651">"تطبيق التصحيح: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="select_application" msgid="2543228890535466325">"اختيار تطبيق"</string> <string name="no_application" msgid="9038334538870247690">"لا شيء"</string> @@ -381,9 +381,9 @@ <string name="local_backup_password_title" msgid="4631017948933578709">"كلمة مرور احتياطية للكمبيوتر"</string> <string name="local_backup_password_summary_none" msgid="7646898032616361714">"النُسخ الاحتياطية الكاملة لسطح المكتب غير محمية في الوقت الحالي"</string> <string name="local_backup_password_summary_change" msgid="1707357670383995567">"انقر لتغيير كلمة مرور النسخ الاحتياطية الكاملة لسطح المكتب أو إزالتها."</string> - <string name="local_backup_password_toast_success" msgid="4891666204428091604">"تم تعيين كلمة مرور احتياطية جديدة"</string> + <string name="local_backup_password_toast_success" msgid="4891666204428091604">"تم ضبط كلمة مرور احتياطية جديدة"</string> <string name="local_backup_password_toast_confirmation_mismatch" msgid="2994718182129097733">"كلمة المرور الجديدة وتأكيدها لا يتطابقان"</string> - <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"تعذّر تعيين كلمة مرور احتياطية"</string> + <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"تعذّر ضبط كلمة مرور احتياطية"</string> <string name="loading_injected_setting_summary" msgid="8394446285689070348">"جارٍ التحميل…"</string> <string-array name="color_mode_names"> <item msgid="3836559907767149216">"نابض بالحياة (تلقائي)"</item> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 8f71509772fc..7f6237da9ee0 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -212,7 +212,7 @@ <string name="adb_wireless_settings" msgid="2295017847215680229">"Адладка па Wi-Fi"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Каб праглядаць і выкарыстоўваць даступныя прылады, уключыце адладку па Wi-Fi"</string> <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Спалучыць прыладу з дапамогай QR-кода"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Спалучаць новыя прылады з дапамогай сканера QR-кода"</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Спалучаць новыя прылады з дапамогай сканера QR-кодаў"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Спалучыць прыладу з дапамогай кода спалучэння"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Спалучаць новыя прылады з дапамогай шасцізначнага кода"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Спалучаныя прылады"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 87f3b7a4f2e8..1ab88ed8e00e 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -487,8 +487,8 @@ <string name="status_unavailable" msgid="5279036186589861608">"অনুপলভ্য"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC র্যান্ডমাইজ করা হয়েছে"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> - <item quantity="one">%1$dটি ডিভাইস কানেক্ট</item> - <item quantity="other">%1$dটি ডিভাইস কানেক্ট</item> + <item quantity="one">%1$dটি ডিভাইস কানেক্ট রয়েছে</item> + <item quantity="other">%1$dটি ডিভাইস কানেক্ট রয়েছে</item> </plurals> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"আরও বেশি।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"আরও কম।"</string> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index 7c2a0fd0c8e4..775cd15edc0b 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -187,7 +187,7 @@ <item msgid="97587758561106269">"Isključeno"</item> <item msgid="7126170197336963369">"Međuspremnici svih zapisnika"</item> <item msgid="7167543126036181392">"Međuspremnici svih zapisnika osim radija"</item> - <item msgid="5135340178556563979">"samo međuspremnik zapisnika kernela"</item> + <item msgid="5135340178556563979">"samo međumemorija zapisnika kernela"</item> </string-array> <string-array name="window_animation_scale_entries"> <item msgid="2675263395797191850">"Animacija isključena"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index dcdadbdf3454..4a11aa77c7fc 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -400,7 +400,7 @@ <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktibo. Aldatzeko, sakatu hau."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Egonean moduko aplikazioaren egoera: <xliff:g id="BUCKET"> %s</xliff:g>"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Abian diren zerbitzuak"</string> - <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu unean abian diren zerbitzuak"</string> + <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu une honetan abian diren zerbitzuak"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView inplementazioa"</string> <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Ezarri WebView inplementazioa"</string> <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"Jada ez dago erabilgarri aukera hori. Saiatu berriro."</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 5fc311af26b2..3373f815ebaf 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -211,8 +211,8 @@ <string name="adb_wireless_error" msgid="721958772149779856">"خطا"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"اشکالزدایی بیسیم"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"برای مشاهده و استفاده از دستگاههای در دسترس، اشکالزدایی بیسیم را روشن کنید"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با کد QR"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاههای جدید را بااستفاده از اسکنر کد QR مرتبط کنید"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با رمزینه پاسخسریع"</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاههای جدید را بااستفاده از اسکنر رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"مرتبط کردن دستگاه با کد مرتبطسازی"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"دستگاههای جدید را با استفاده از کد شش رقمی مرتبط کنید"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"دستگاههای مرتبطشده"</string> @@ -226,12 +226,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"کد مرتبطسازی Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"مرتبطسازی ناموفق"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"مطمئن شوید که دستگاه به همان شبکه متصل باشد."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن کد QR مرتبط کنید"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"مرتبطسازی دستگاه…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا کد QR اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا رمزینه پاسخسریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"نشانی IP و درگاه"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن کد QR"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن کد QR مرتبط کنید"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن رمزینه پاسخسریع"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"لطفاً به شبکه Wi-Fi متصل شوید"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"ADB (پل اشکالزدایی Android)، اشکالزدایی کردن، برنامهنویس"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"میانبر گزارش مشکل"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 3261f69b1105..894a14ac3b74 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -487,8 +487,8 @@ <string name="status_unavailable" msgid="5279036186589861608">"અનુપલબ્ધ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MACને રેન્ડમ કરેલ છે"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> - <item quantity="one">%1$d ઉપકરણ કનેક્ટ કર્યું</item> - <item quantity="other">%1$d ઉપકરણો કનેક્ટ કર્યા</item> + <item quantity="one">%1$d ડિવાઇસ કનેક્ટ કર્યું</item> + <item quantity="other">%1$d ડિવાઇસ કનેક્ટ કર્યા</item> </plurals> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"વધુ સમય."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ઓછો સમય."</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 904a70e73958..6e734bc873b8 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -487,7 +487,7 @@ <string name="status_unavailable" msgid="5279036186589861608">"मौजूद नहीं है"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"एमएसी पता रैंडम पर सेट है"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> - <item quantity="one">%1$d डिवाइस जुड़े हैं</item> + <item quantity="one">%1$d डिवाइस जुड़ा है</item> <item quantity="other">%1$d डिवाइस जुड़े हैं</item> </plurals> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ज़्यादा समय."</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index fec2dd6d523f..d86d88b3345d 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -145,7 +145,7 @@ <string name="data_usage_ota" msgid="7984667793701597001">"Rendszerfrissítések"</string> <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB-megosztás"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hordozható hotspot"</string> - <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth megosztása"</string> + <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth-megosztás"</string> <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Megosztás"</string> <string name="tether_settings_title_all" msgid="8910259483383010470">"Megosztás és hotspot"</string> <string name="managed_user_title" msgid="449081789742645723">"Összes munkaalkalmazás"</string> diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml index 2696f5a01785..a279872a088e 100644 --- a/packages/SettingsLib/res/values-hy/arrays.xml +++ b/packages/SettingsLib/res/values-hy/arrays.xml @@ -55,7 +55,7 @@ </string-array> <string-array name="hdcp_checking_summaries"> <item msgid="4045840870658484038">"Երբեք չօգտագործել HDCP ստուգումը"</item> - <item msgid="8254225038262324761">"Օգտագործել HDCP-ը` միայն DRM-ի բովանդակությունը ստուգելու համար"</item> + <item msgid="8254225038262324761">"Օգտագործել HDCP-ը՝ միայն DRM-ի բովանդակությունը ստուգելու համար"</item> <item msgid="6421717003037072581">"Միշտ օգտագործել HDCP ստուգումը"</item> </string-array> <string-array name="bt_hci_snoop_log_entries"> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 8b92640e9996..e434cac36b34 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -57,7 +57,7 @@ <string name="osu_sign_up_complete" msgid="7640183358878916847">"Գրանցումն ավարտված է: Միացում…"</string> <string name="speed_label_very_slow" msgid="8526005255731597666">"Շատ դանդաղ"</string> <string name="speed_label_slow" msgid="6069917670665664161">"Դանդաղ"</string> - <string name="speed_label_okay" msgid="1253594383880810424">"Հաստատել"</string> + <string name="speed_label_okay" msgid="1253594383880810424">"Լավ"</string> <string name="speed_label_medium" msgid="9078405312828606976">"Միջին"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Արագ"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Շատ արագ"</string> @@ -343,7 +343,7 @@ <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string> <string name="debug_hw_overdraw" msgid="8944851091008756796">"Վրիպազերծել GPU գերազանցումները"</string> <string name="disable_overlays" msgid="4206590799671557143">"Կասեցնել HW վրադրումները"</string> - <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն` էկրանի կազմման համար"</string> + <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն՝ էկրանի կազմման համար"</string> <string name="simulate_color_space" msgid="1206503300335835151">"Նմանակել գունատարածքը"</string> <string name="enable_opengl_traces_title" msgid="4638773318659125196">"Ակտիվացնել OpenGL հետքերը"</string> <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Անջատել USB աուդիո երթուղումը"</string> @@ -493,7 +493,7 @@ <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Ավելացնել ժամանակը:"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Պակասեցնել ժամանակը:"</string> <string name="cancel" msgid="5665114069455378395">"Չեղարկել"</string> - <string name="okay" msgid="949938843324579502">"Հաստատել"</string> + <string name="okay" msgid="949938843324579502">"Եղավ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Միացնել"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Միացրեք «Չանհանգստացնել» ռեժիմը"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Երբեք"</string> diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml index 743017c82e3a..7a4e71b18ec0 100644 --- a/packages/SettingsLib/res/values-ja/arrays.xml +++ b/packages/SettingsLib/res/values-ja/arrays.xml @@ -243,7 +243,7 @@ </string-array> <string-array name="track_frame_time_entries"> <item msgid="634406443901014984">"OFF"</item> - <item msgid="1288760936356000927">"バーとして画面に表示"</item> + <item msgid="1288760936356000927">"棒グラフとして画面に表示"</item> <item msgid="5023908510820531131">"<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g> 内"</item> </string-array> <string-array name="debug_hw_overdraw_entries"> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 696ed2955daa..f13d9b860dce 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -538,7 +538,7 @@ <string name="user_setup_button_setup_now" msgid="1708269547187760639">"지금 설정"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"나중에"</string> <string name="user_add_user_type_title" msgid="551279664052914497">"추가"</string> - <string name="user_new_user_name" msgid="60979820612818840">"새 사용자"</string> + <string name="user_new_user_name" msgid="60979820612818840">"신규 사용자"</string> <string name="user_new_profile_name" msgid="2405500423304678841">"새 프로필"</string> <string name="user_info_settings_title" msgid="6351390762733279907">"사용자 정보"</string> <string name="profile_info_settings_title" msgid="105699672534365099">"프로필 정보"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index c4b5f7e7477f..419c18f3b8c6 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -396,8 +396,8 @@ <item msgid="1282170165150762976">"Санарип мазмун үчүн оптималдаштырылган түстөр"</item> </string-array> <string name="inactive_apps_title" msgid="5372523625297212320">"Көшүү режиминдеги колдонмолор"</string> - <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Которуштуруу үчүн таптап коюңуз."</string> - <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Которуштуруу үчүн таптап коюңуз."</string> + <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Күйгүзүү үчүн басып коюңуз."</string> + <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Өчүрүү үчүн басып коюңуз."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Көшүү режиминдеги колдонмонун абалы:<xliff:g id="BUCKET"> %s</xliff:g>"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Иштеп жаткан кызматтар"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Учурда иштеп жаткан кызматтарды көрүп, көзөмөлдөп турасыз"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index fb7b63410a5f..58d4af1c2c64 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -204,10 +204,10 @@ <string name="tethering_settings_not_available" msgid="266821736434699780">"Поставките за спојување не се достапни за овој корисник"</string> <string name="apn_settings_not_available" msgid="1147111671403342300">"Поставките за името на пристапната точка не се достапни за овој корисник"</string> <string name="enable_adb" msgid="8072776357237289039">"Отстранување грешки на USB"</string> - <string name="enable_adb_summary" msgid="3711526030096574316">"Режим на отстранување грешки кога е поврзано USB"</string> + <string name="enable_adb_summary" msgid="3711526030096574316">"Режим за отстранување грешки кога е поврзано USB"</string> <string name="clear_adb_keys" msgid="3010148733140369917">"Отповикај овластувања за отстранување грешки од USB"</string> <string name="enable_adb_wireless" msgid="6973226350963971018">"Безжично отстранување грешки"</string> - <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Режим на отстранување грешки кога е поврзано Wi‑Fi"</string> + <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Режим за отстранување грешки кога е поврзано Wi‑Fi"</string> <string name="adb_wireless_error" msgid="721958772149779856">"Грешка"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Безжично отстранување грешки"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"За да ги гледате и користите достапните уреди, вклучете го безжичното отстранување грешки"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 762d8304dd64..ee368fd5bfcf 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -143,9 +143,9 @@ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"हटाइएका एपहरू"</string> <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"एपहरू र प्रयोगकर्ताहरू हटाइयो।"</string> <string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string> - <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेथर गर्दै"</string> + <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेदर गर्दै"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"पोर्टेबल हटस्पट"</string> - <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेथर गर्दै"</string> + <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेदर गर्दै"</string> <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"टेदर गर्दै"</string> <string name="tether_settings_title_all" msgid="8910259483383010470">"टेदर गर्ने र पोर्टेबल हटस्पट"</string> <string name="managed_user_title" msgid="449081789742645723">"कार्य प्रोफाइलका सबै एपहरू"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index d2891a06402a..c5d70ac866b1 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -143,11 +143,11 @@ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Programu zilizoondolewa"</string> <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Watumiaji na programu ziilizoondolewa"</string> <string name="data_usage_ota" msgid="7984667793701597001">"Masasisho ya mfumo"</string> - <string name="tether_settings_title_usb" msgid="3728686573430917722">"Shiriki intaneti kwa USB"</string> + <string name="tether_settings_title_usb" msgid="3728686573430917722">"Sambaza mtandao kwa USB"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Intaneti ya kusambazwa"</string> - <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Shiriki intaneti kwa Bluetooth"</string> - <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Inazuia"</string> - <string name="tether_settings_title_all" msgid="8910259483383010470">"Kushiriki na kusambaza intaneti"</string> + <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Sambaza mtandao kwa Bluetooth"</string> + <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Kusambaza mtandao"</string> + <string name="tether_settings_title_all" msgid="8910259483383010470">"Kushiriki na kusambaza mtandao"</string> <string name="managed_user_title" msgid="449081789742645723">"Programu zote za kazini"</string> <string name="user_guest" msgid="6939192779649870792">"Mgeni"</string> <string name="unknown" msgid="3544487229740637809">"Haijulikani"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 60001a0b0ecd..e252eca5d72a 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -510,7 +510,7 @@ <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string> - <string name="help_label" msgid="3528360748637781274">"సహాయం & అభిప్రాయం"</string> + <string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string> <string name="storage_category" msgid="2287342585424631813">"నిల్వ"</string> <string name="shared_data_title" msgid="1017034836800864953">"షేర్ చేసిన డేటా"</string> <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, సవరించండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index defc33ee9233..7468d0450a7e 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -143,7 +143,7 @@ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"แอปพลิเคชันที่นำออก"</string> <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"แอปพลิเคชันและผู้ใช้ที่นำออก"</string> <string name="data_usage_ota" msgid="7984667793701597001">"การอัปเดตระบบ"</string> - <string name="tether_settings_title_usb" msgid="3728686573430917722">"ปล่อยสัญญาณผ่าน USB"</string> + <string name="tether_settings_title_usb" msgid="3728686573430917722">"เชื่อมต่อเน็ตผ่าน USB"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"ฮอตสปอตแบบพกพาได้"</string> <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ปล่อยสัญญาณบลูทูธ"</string> <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"การปล่อยสัญญาณ"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9d06c8467e41..72a6074ff89c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -465,7 +465,16 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); - mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + // Add disconnected bluetooth devices only when phone output device is available. + for (MediaDevice device : devices) { + final int type = device.getDeviceType(); + if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + break; + } + } } final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java index bba69f29a290..aad0d3af6626 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java @@ -35,7 +35,6 @@ import androidx.preference.PreferenceViewHolder; import com.android.settingslib.R; import com.android.settingslib.Utils; import com.android.wifitrackerlib.WifiEntry; -import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; /** * Preference to display a WifiEntry in a wifi picker. @@ -138,11 +137,7 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt public void refresh() { setTitle(mWifiEntry.getTitle()); final int level = mWifiEntry.getLevel(); - final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo(); - boolean showX = false; - if (connectedInfo != null) { - showX = !connectedInfo.isDefaultNetwork || !connectedInfo.isValidated; - } + final boolean showX = mWifiEntry.shouldShowXLevelIcon(); if (level != mLevel || showX != mShowX) { mLevel = level; mShowX = showX; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index a654fd47ea12..8e850b25159c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -585,6 +585,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -683,6 +684,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java index 40af7dc797b3..c21830b28e3a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java @@ -17,7 +17,6 @@ package com.android.settingslib.wifi; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; @@ -30,7 +29,6 @@ import androidx.preference.PreferenceViewHolder; import com.android.settingslib.R; import com.android.wifitrackerlib.WifiEntry; -import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; import org.junit.Before; import org.junit.Test; @@ -179,43 +177,9 @@ public class WifiEntryPreferenceTest { } @Test - public void levelChanged_notDefaultWifiRefresh_shouldUpdateLevelIcon() { + public void levelChanged_showXWifiRefresh_shouldUpdateLevelIcon() { final List<Drawable> iconList = new ArrayList<>(); - final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class); - mockConnectedInfo.isDefaultNetwork = false; - when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo); - final WifiEntryPreference pref = - new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); - - when(mMockWifiEntry.getLevel()).thenReturn(0); - pref.refresh(); - iconList.add(pref.getIcon()); - when(mMockWifiEntry.getLevel()).thenReturn(1); - pref.refresh(); - iconList.add(pref.getIcon()); - when(mMockWifiEntry.getLevel()).thenReturn(2); - pref.refresh(); - iconList.add(pref.getIcon()); - when(mMockWifiEntry.getLevel()).thenReturn(3); - pref.refresh(); - iconList.add(pref.getIcon()); - when(mMockWifiEntry.getLevel()).thenReturn(4); - pref.refresh(); - iconList.add(pref.getIcon()); - when(mMockWifiEntry.getLevel()).thenReturn(-1); - pref.refresh(); - iconList.add(pref.getIcon()); - - assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1, - mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null); - } - - @Test - public void levelChanged_notValidatedWifiRefresh_shouldUpdateLevelIcon() { - final List<Drawable> iconList = new ArrayList<>(); - final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class); - mockConnectedInfo.isValidated = false; - when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo); + when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true); final WifiEntryPreference pref = new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index efaef2191987..3055104714a1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -165,9 +165,10 @@ public class SecureSettings { Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, Settings.Secure.PEOPLE_STRIP, Settings.Secure.MEDIA_CONTROLS_RESUME, - Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 6436355255a8..f1846db9d7b5 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -244,8 +244,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME_BLOCKED, - COLON_SEPARATED_PACKAGE_LIST_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE, new InclusiveIntegerRangeValidator( Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, @@ -254,5 +252,8 @@ public class SecureSettingsValidators { Secure.ACCESSIBILITY_BUTTON_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put( + Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR); + VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 845c0a3847c1..5977ebe5e142 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1878,6 +1878,15 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE); p.end(assistToken); + final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES); + dumpSetting(s, p, + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + SecureSettingsProto.AssistHandles.LEARNING_TIME_ELAPSED_MILLIS); + dumpSetting(s, p, + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + SecureSettingsProto.AssistHandles.LEARNING_EVENT_COUNT); + p.end(assistHandlesToken); + final long autofillToken = p.start(SecureSettingsProto.AUTOFILL); dumpSetting(s, p, Settings.Secure.AUTOFILL_SERVICE, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 7b8aba451e27..6e74184cef02 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -113,6 +113,7 @@ <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MONITOR_INPUT" /> + <uses-permission android:name="android.permission.INPUT_CONSUMER" /> <!-- DreamManager --> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> @@ -720,10 +721,9 @@ <service android:name=".controls.controller.AuxiliaryPersistenceWrapper$DeletionJobService" android:permission="android.permission.BIND_JOB_SERVICE"/> - <!-- started from ControlsFavoritingActivity --> + <!-- started from ControlsRequestReceiver --> <activity android:name=".controls.management.ControlsRequestDialog" - android:exported="true" android:theme="@style/Theme.ControlsRequestDialog" android:finishOnCloseSystemDialogs="true" android:showForAllUsers="true" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java index 7463d3fd3650..8f24e7927e3a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java @@ -75,6 +75,11 @@ public interface NotificationMenuRowPlugin extends Plugin { public MenuItem getLongpressMenuItem(Context context); /** + * @return the {@link MenuItem} to display when app ops icons are pressed. + */ + public MenuItem getAppOpsMenuItem(Context context); + + /** * @return the {@link MenuItem} to display when snooze item is pressed. */ public MenuItem getSnoozeMenuItem(Context context); diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index 6d86a78360d8..65e3f0dbd176 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -112,7 +112,7 @@ <string name="kg_pin_accepted" msgid="1625501841604389716">"تم قبول الرمز"</string> <string name="keyguard_carrier_default" msgid="6359808469637388586">"لا تتوفر خدمة."</string> <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string> - <string name="airplane_mode" msgid="2528005343938497866">"وضع الطائرة"</string> + <string name="airplane_mode" msgid="2528005343938497866">"وضع الطيران"</string> <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string> <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string> <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 24b5c23a6732..52b7fab768c5 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -100,7 +100,7 @@ <string name="kg_pin_accepted" msgid="1625501841604389716">"កូដត្រូវបានទទួលយក!"</string> <string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មានសេវាទេ។"</string> <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរវិធីបញ្ចូល"</string> - <string name="airplane_mode" msgid="2528005343938497866">"មុខងារពេលជិះយន្តហោះ"</string> + <string name="airplane_mode" msgid="2528005343938497866">"ពេលជិះយន្តហោះ"</string> <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 74386bc7a487..d44003bc5b95 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -134,7 +134,7 @@ <item quantity="other">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం కారియర్ను సంప్రదించండి.</item> <item quantity="one">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది వివరాల కోసం కారియర్ను సంప్రదించండి.</item> </plurals> - <string name="clock_title_default" msgid="6342735240617459864">"డిఫాల్ట్"</string> + <string name="clock_title_default" msgid="6342735240617459864">"ఆటోమేటిక్"</string> <string name="clock_title_bubble" msgid="2204559396790593213">"బబుల్"</string> <string name="clock_title_analog" msgid="8409262532900918273">"ఎనలాగ్"</string> </resources> diff --git a/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml new file mode 100644 index 000000000000..998db3b44b19 --- /dev/null +++ b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml @@ -0,0 +1,36 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/> + <path android:fillColor="#FF000000" + android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/> + </group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/> + </group> + <path android:fillColor="#FF000000" + android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/> + </group> +</vector> + diff --git a/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml new file mode 100644 index 000000000000..998db3b44b19 --- /dev/null +++ b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml @@ -0,0 +1,36 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/> + <path android:fillColor="#FF000000" + android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/> + </group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/> + </group> + <path android:fillColor="#FF000000" + android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/> + </group> +</vector> + diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index ae7f44d19430..b9e711e54b3b 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -50,7 +50,7 @@ <FrameLayout android:layout_width="match_parent" - android:layout_height="72dp"> + android:layout_height="@dimen/controls_management_footer_height"> <View android:layout_width="match_parent" @@ -61,7 +61,8 @@ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="@dimen/controls_management_footer_side_margin"> + android:paddingHorizontal="@dimen/controls_management_footer_side_margin" + android:paddingVertical="@dimen/controls_management_footer_top_margin" > <Button android:id="@+id/other_apps" diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml index 4850e7534943..0ddd0e38acb9 100644 --- a/packages/SystemUI/res/layout/controls_management_favorites.xml +++ b/packages/SystemUI/res/layout/controls_management_favorites.xml @@ -36,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="@dimen/controls_management_page_indicator_height" android:layout_gravity="center" - android:layout_marginTop="@dimen/controls_management_list_margin" + android:layout_marginTop="@dimen/controls_management_indicator_top_margin" android:visibility="invisible" /> <androidx.viewpager2.widget.ViewPager2 diff --git a/packages/SystemUI/res/layout/controls_structure_page.xml b/packages/SystemUI/res/layout/controls_structure_page.xml index f048d62d46d7..412ed566ff1b 100644 --- a/packages/SystemUI/res/layout/controls_structure_page.xml +++ b/packages/SystemUI/res/layout/controls_structure_page.xml @@ -21,4 +21,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:layout_marginTop="@dimen/controls_management_zone_top_margin"/>
\ No newline at end of file + android:layout_marginTop="@dimen/controls_management_favorites_top_margin"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index ed870f8bb2ef..170f2c4e0bea 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -166,8 +166,7 @@ android:layout_height="wrap_content" android:clickable="true" android:maxHeight="@dimen/qs_media_enabled_seekbar_height" - android:paddingTop="16dp" - android:paddingBottom="16dp" + android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding" android:thumbTint="@color/media_primary_text" android:progressTint="@color/media_seekbar_progress" android:progressBackgroundTint="@color/media_disabled" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 51397657f305..a72c9aa7a176 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Bo 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Bo 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Volskerm onder"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dubbeltik om te wysig."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om by te voeg."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Verwyder <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Voeg <xliff:g id="TILE_NAME">%1$s</xliff:g> by posisie <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g> na posisie <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"voeg teël aan einde by"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Voeg teël by"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Program sal dalk nie met verdeelde skerm werk nie."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index dad126cedee3..d6838609fc08 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ከላይ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ከላይ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"የታች ሙሉ ማያ ገጽ"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ቦታ <xliff:g id="POSITION">%1$d</xliff:g>፣ <xliff:g id="TILE_NAME">%2$s</xliff:g>። ለማርትዕ ሁለቴ መታ ያድርጉ።"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>። ለማከል ሁለቴ መታ ያድርጉ።"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ይውሰዱ"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ያስወግዱ"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ወደ አቀማመጥ <xliff:g id="POSITION">%2$d</xliff:g> አክል"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ወደ አቀማመጥ <xliff:g id="POSITION">%2$d</xliff:g> አንቀሳቅስ"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ሰቅ መጨረሻው ላይ አክል"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ሰቅ ያክሉ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index f906cf2a8f93..c5d52eea74f5 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -232,7 +232,7 @@ <string name="not_default_data_content_description" msgid="6757881730711522517">"لم يتم الضبط على استخدام البيانات"</string> <string name="cell_data_off" msgid="4886198950247099526">"غير مفعّلة"</string> <string name="accessibility_bluetooth_tether" msgid="6327291292208790599">"التوصيل عبر البلوتوث"</string> - <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطائرة."</string> + <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطيران."</string> <string name="accessibility_vpn_on" msgid="8037549696057288731">"الشبكة الافتراضية الخاصة (VPN) قيد التفعيل."</string> <string name="accessibility_no_sims" msgid="5711270400476534667">"ليس هناك شريحة SIM."</string> <string name="carrier_network_change_mode" msgid="5174141476991149918">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string> @@ -267,10 +267,10 @@ <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"تم تفعيل Wifi."</string> <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"الجوّال <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string> <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"البطارية <xliff:g id="STATE">%s</xliff:g>."</string> - <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"إيقاف وضع الطائرة."</string> - <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"تفعيل وضع الطائرة."</string> - <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطائرة."</string> - <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطائرة."</string> + <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"إيقاف وضع الطيران."</string> + <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"تفعيل وضع الطيران."</string> + <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطيران."</string> + <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطيران."</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"كتم الصوت تمامًا"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"المنبِّهات فقط"</string> <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"عدم الإزعاج"</string> @@ -318,7 +318,7 @@ <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"تم الوصول إلى حد البيانات الذي عيَّنته. لم يُعد بإمكانك استخدام بيانات الجوال.\n\nفي حالة الاستئناف، قد يتم تطبيق الرسوم لاستخدام البيانات."</string> <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"استئناف"</string> <string name="gps_notification_searching_text" msgid="231304732649348313">"جارٍ البحث عن GPS"</string> - <string name="gps_notification_found_text" msgid="3145873880174658526">"تم تعيين الموقع بواسطة GPS"</string> + <string name="gps_notification_found_text" msgid="3145873880174658526">"تم ضبط الموقع بواسطة GPS"</string> <string name="accessibility_location_active" msgid="2845747916764660369">"طلبات الموقع نشطة"</string> <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"أجهزة الاستشعار غير مفعّلة"</string> <string name="accessibility_clear_all" msgid="970525598287244592">"محو جميع الإشعارات."</string> @@ -664,7 +664,7 @@ <string name="status_bar_ethernet" msgid="5690979758988647484">"إيثرنت"</string> <string name="status_bar_alarm" msgid="87160847643623352">"المنبّه"</string> <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string> - <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطائرة"</string> + <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string> <string name="add_tile" msgid="6239678623873086686">"إضافة فئة"</string> <string name="broadcast_tile" msgid="5224010633596487481">"إرسال فئة"</string> <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g> إلا إذا أوقفت هذا قبل الموعد"</string> @@ -697,7 +697,7 @@ <string name="tuner_full_importance_settings" msgid="1388025816553459059">"عناصر التحكم في إشعارات التشغيل"</string> <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"تشغيل"</string> <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"إيقاف"</string> - <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك تعيين مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string> + <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك ضبط مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string> <string name="notification_header_default_channel" msgid="225454696914642444">"الإشعارات"</string> <string name="notification_channel_disabled" msgid="928065923928416337">"لن تتلقى هذه الإشعارات بعد الآن."</string> <string name="notification_channel_minimized" msgid="6892672757877552959">"سيتم تصغير هذه الإشعارات."</string> @@ -902,12 +902,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ضبط حجم النافذة العلوية ليكون ٥٠%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ضبط حجم النافذة العلوية ليكون ٣٠%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"عرض النافذة السفلية بملء الشاشة"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"الموضع <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>. انقر مرّتين للتعديل."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. انقر مرّتين للإضافة."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"إزالة <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"إضافة <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة بطاقة"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى نهاية الإعدادات السريعة"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل بطاقة"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"إضافة بطاقة"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"يمكن ألا يعمل التطبيق مع وضع تقسيم الشاشة."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 9185d95bf9dd..382496c11d42 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"তলৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"অৱস্থান <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। সম্পাদনা কৰিবৰ বাবে দুবাৰ টিপক।"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। যোগ কৰিবলৈ দুবাৰ টিপক।"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> স্থানান্তৰ কৰক"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ক আঁতৰাওক"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"এই স্থান <xliff:g id="POSITION">%2$d</xliff:g>ত <xliff:g id="TILE_NAME">%1$s</xliff:g> যোগ কৰক"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ক এই স্থান <xliff:g id="POSITION">%2$d</xliff:g>লৈ যাওক"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"টাইল শেষত যোগ দিবলৈ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ দিয়ক"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"বিভাজিত স্ক্ৰীণৰ সৈতে এপে হয়তো কাম নকৰিব।"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 933bb044b5dc..acd6ddbdc482 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Yuxarı 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Yuxarı 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Aşağı tam ekran"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Redaktə etmək üçün iki dəfə tıklayın."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Əlavə etmək üçün iki dəfə tıklayın."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> silin"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> pozisiyasına <xliff:g id="TILE_NAME">%1$s</xliff:g> əlavə edin"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> pozisiyasına <xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"sona lövhə əlavə edin"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lövhə əlavə edin"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index abb8a3a9122d..e5f7c7dade76 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -887,12 +887,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gornji ekran 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gornji ekran 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Režim celog ekrana za donji ekran"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvaput dodirnite da biste izmenili."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dvaput dodirnite da biste dodali."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premesti pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ukloni pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodajte „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premestite „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodali pločicu na kraj"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajte pločicu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija možda neće funkcionisati sa podeljenim ekranom."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 77c1606d4130..b79d943c0b73 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -720,7 +720,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя. Размовы ў праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартна паяўляюцца ў выглядзе ўсплывальных апавяшчэнняў."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Прыцягвае ўвагу да гэтага змесціва ўсплывальнай кнопкай."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў у выглядзе ўсплывальнага апавяшчэння, а на экране блакіроўкі – у выглядзе відарыса профілю"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў, як усплывальнае апавяшчэнне, паказвае фота профілю на экране блакіроўкі"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Налады"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае функцыі размовы"</string> @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Верхні экран – 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Верхні экран – 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ніжні экран – поўнаэкранны рэжым"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Месца: <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Краніце двойчы, каб рэдагаваць."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Краніце двойчы, каб дадаць."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Перамясціць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Выдаліць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Дадаць \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" у наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Перамясціць \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" у наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дадаць плітку ў канец"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Дадаць плітку"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Праграма можа не працаваць у рэжыме дзялення экрана."</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6ff1bfcee29c..e742ab888d23 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да звъни или да вибрира въз основа на настройките за телефона"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звъни или да вибрира въз основа на настройките за телефона. Разговорите от <xliff:g id="APP_NAME">%1$s</xliff:g> се показват като балончета по подразбиране."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Задържа вниманието ви посредством плаващ пряк път към това съдържание."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори и показва снимката на потребителския профил на заключения екран"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори, показва снимката на потр. профил на заключения екран"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддържа функциите за разговор"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Горен екран: 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Горен екран: 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Долен екран: Показване на цял екран"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>, „<xliff:g id="TILE_NAME">%2$s</xliff:g>“. Докоснете двукратно, за да редактирате."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"„<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Докоснете двукратно, за да добавите."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Преместване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Премахване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Добавете „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместете „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавяне на панел в края"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавяне на панел"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Приложението може да не работи в режим на разделен екран."</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 24805e067d12..22c83cc89126 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"শীর্ষ ৫০%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"শীর্ষ ৩০%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"নীচের অংশ নিয়ে পূর্ণ স্ক্রিন"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> লোকেশন, <xliff:g id="TILE_NAME">%2$s</xliff:g>৷ সম্পাদনা করতে দুবার আলতো চাপুন৷"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>৷ যোগ করতে দুবার আলতো চাপুন৷"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>-এ যোগ করুন"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>-এ সরান"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল সরান"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"শেষে টাইল যোগ করুন"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল সরান"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ করুন"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"অ্যাপ্লিকেশানটি বিভক্ত স্ক্রীনে কাজ নাও করতে পারে৷"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 830929cf2d00..f70d3fde1048 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -887,12 +887,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gore 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gore 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Donji ekran kao cijeli ekran"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dodirnite dvaput za uređivanje."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> Dodirnite dvaput za dodavanje."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pomjeri <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ukloni <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodaj <xliff:g id="TILE_NAME">%1$s</xliff:g> na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g> na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija možda neće raditi na podijeljenom ekranu"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 78ab320b6556..9d33dd6e7aa3 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -92,10 +92,10 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Vols iniciar la gravació?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"Quan graves contingut, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou les contrasenyes, la informació de pagament, les fotos, els missatges i l\'àudio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"Durant la gravació, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou contrasenyes, informació de pagament, fotos, missatges i àudio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Àudio del dispositiu"</string> - <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons del dispositiu, com ara la música, les trucades i els sons de trucada"</string> + <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"So del dispositiu, com ara música, trucades i sons de trucada"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Micròfon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Àudio del dispositiu i micròfon"</string> <string name="screenrecord_start" msgid="330991441575775004">"Inicia"</string> @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"AUTOMÀTICA"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix els colors"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix colors"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"Mode de correcció de color"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string> <string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string> @@ -707,7 +707,7 @@ <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactiva les notificacions"</string> <string name="inline_keep_showing_app" msgid="4393429060390649757">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Silenci"</string> - <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string> + <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminat"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Bombolla"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sense so ni vibració i es mostra més avall a la secció de converses"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Pantalla superior al 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Pantalla superior al 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla inferior completa"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posició <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Fes doble toc per editar-la."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Fes doble toc per afegir-ho."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Suprimeix <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Afegeix <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir un mosaic al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix un mosaic"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 9d661badd48e..ba2aad83cd4c 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -720,7 +720,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> ve výchozím nastavení bublají."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Přitahuje pozornost pomocí plovoucí zkratky k tomuto obsahu."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzace a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzací a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nastavení"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> funkce konverzace nepodporuje"</string> @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % nahoře"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % nahoře"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Dolní část na celou obrazovku"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvojitým klepnutím ji upravíte."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dlaždici přidáte dvojitým klepnutím."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Odstranit dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Přidat dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranit dlaždici"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"přidat dlaždici na konec"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Přidat dlaždici"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string> @@ -1033,7 +1034,7 @@ <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Pohotovostní režim"</string> <string name="priority_onboarding_title" msgid="2893070698479227616">"Konverzace byla nastavena jako prioritní"</string> <string name="priority_onboarding_behavior" msgid="5342816047020432929">"Chování prioritních konverzací:"</string> - <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Zobrazovat v horní části sekce konverzace"</string> + <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Zobrazovat v horní části sekce konverzací"</string> <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Zobrazovat profilovou fotku na zámku obrazovky"</string> <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Zobrazuje se jako plovoucí bublina nad aplikacemi"</string> <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Přerušit režim Nerušit"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 30f92c039377..c119d8004da4 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringe eller vibrere baseret på telefonens indstillinger"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere baseret på telefonens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Fastholder din opmærksomhed med en svævende genvej til indholdet."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen, som en svævende boble og med profilbillede på låseskærmen"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen som en svævende boble og med profilbillede på låseskærmen"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Indstillinger"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> understøtter ikke samtalefunktioner"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Øverste 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Øverste 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Vis nederste del i fuld skærm"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryk to gange for at redigere."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tryk to gange for at tilføje."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Føj <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern kortet"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"føj kortet til slutningen"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt kortet"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tilføj et kort"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Appen fungerer muligvis ikke i opdelt skærm."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index e4f5ac2f0e25..82b8b09778e4 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -28,15 +28,15 @@ <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string> <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string> + <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string> <string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string> <string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string> <string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string> <string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string> - <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string> - <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string> + <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string> + <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string> <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string> - <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string> + <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string> <string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string> <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string> <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string> @@ -419,7 +419,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string> @@ -497,9 +497,9 @@ <string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string> <string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string> <string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string> - <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string> + <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string> - <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string> + <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string> <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string> @@ -765,8 +765,8 @@ <item quantity="one">%d Minute</item> </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string> - <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string> + <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % oben"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % oben"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Vollbild unten"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Zum Bearbeiten doppeltippen."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Zum Hinzufügen doppeltippen."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verschieben"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> entfernen"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> auf Position <xliff:g id="POSITION">%2$d</xliff:g> hinzufügen"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> auf Position <xliff:g id="POSITION">%2$d</xliff:g> verschieben"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"die Kachel zu entfernen"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"die Kachel am Ende hinzuzufügen"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Kachel hinzufügen"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Die App funktioniert unter Umständen bei geteiltem Bildschirm nicht."</string> @@ -980,11 +981,11 @@ <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string> <string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string> <string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string> - <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string> + <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string> <string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string> - <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string> - <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string> + <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string> + <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string> <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 32b335ec480a..fae718db7bb5 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -691,7 +691,7 @@ <string name="notification_channel_minimized" msgid="6892672757877552959">"Αυτές οι ειδοποιήσεις θα ελαχιστοποιηθούν"</string> <string name="notification_channel_silenced" msgid="1995937493874511359">"Αυτές οι ειδοποιήσεις θα εμφανίζονται σιωπηλά"</string> <string name="notification_channel_unsilenced" msgid="94878840742161152">"Αυτές οι ειδοποιήσεις θα σας ενημερώνουν"</string> - <string name="inline_blocking_helper" msgid="2891486013649543452">"Συνήθως απορρίπτετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string> + <string name="inline_blocking_helper" msgid="2891486013649543452">"Συνήθως παραβλέπετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string> <string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string> <string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"Να συνεχίσουν να εμφανίζονται αυτές οι ειδοποιήσεις;"</string> @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου. Οι συζητήσεις από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανίζονται σε συννεφάκι από προεπιλογή."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Κρατάει την προσοχή σας με μια κινούμενη συντόμευση προς αυτό το περιεχόμενο."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται στο επάνω μέρος της ενότητας συζητήσεων, προβάλλεται ως κινούμενο συννεφάκι, εμφανίζει τη φωτογραφία προφίλ στην οθόνη κλειδώματος"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται επάνω στις συζητήσεις, προβάλλεται ως κιν. συννεφάκι, εμφανίζει τη φωτ. προφίλ στην οθ. κλειδ."</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ρυθμίσεις"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Προτεραιότητα"</string> <string name="no_shortcut" msgid="8257177117568230126">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν υποστηρίζει τις λειτουργίες συζήτησης"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Πάνω 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Πάνω 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Κάτω πλήρης οθόνη"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Πατήστε δύο φορές για επεξεργασία."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Πατήστε δύο φορές για προσθήκη."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Κατάργηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Προσθήκη <xliff:g id="TILE_NAME">%1$s</xliff:g> στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g> στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"προσθήκη πλακιδίου στο τέλος"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Προσθήκη πλακιδίου"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Δεν είναι δυνατή η λειτουργία της εφαρμογής με διαχωρισμό οθόνης."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 3a52a7269186..275aa0b1add6 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 11e0fb9a1852..c9937af0df47 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 3a52a7269186..275aa0b1add6 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 3a52a7269186..275aa0b1add6 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 1a02d596ca14..e206e0fd63be 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index c27bdd0f2c0c..5f9bb50b5271 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -246,7 +246,7 @@ <string name="accessibility_remove_notification" msgid="1641455251495815527">"Eliminar notificación"</string> <string name="accessibility_gps_enabled" msgid="4061313248217660858">"GPS habilitado"</string> <string name="accessibility_gps_acquiring" msgid="896207402196024040">"Adquisición de GPS"</string> - <string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter habilitado"</string> + <string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo habilitado"</string> <string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Timbre en vibración"</string> <string name="accessibility_ringer_silent" msgid="8994620163934249882">"Timbre en silencio"</string> <!-- no translation found for accessibility_casting (8708751252897282313) --> @@ -706,17 +706,17 @@ <string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"Seguir recibiendo alertas"</string> <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactivar notificaciones"</string> <string name="inline_keep_showing_app" msgid="4393429060390649757">"¿Quieres seguir viendo las notificaciones de esta app?"</string> - <string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string> + <string name="notification_silence_title" msgid="8608090968400832335">"Silenciada"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Cuadro"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en una parte inferior de la sección de conversaciones"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono."</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puede sonar o vibrar en función de la configuración del teléfono. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Retiene tu atención con un acceso directo flotante a este contenido."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo."</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaria"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"No hay burbujas recientes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Superior: 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Superior: 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla inferior completa"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Presiona dos veces para editarla."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Presiona dos veces para agregarlo."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Quitar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Agregar <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarjeta"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"agregar tarjeta al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Agregar tarjeta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Es posible que la app no funcione en el modo de pantalla dividida."</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 3cbf9a174fcf..926f27112b4f 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -98,7 +98,7 @@ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sonido de tu dispositivo, como música, llamadas y tonos de llamada"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Micrófono"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Audio y micrófono del dispositivo"</string> - <string name="screenrecord_start" msgid="330991441575775004">"Empezar"</string> + <string name="screenrecord_start" msgid="330991441575775004">"Iniciar"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Grabando pantalla"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Grabando pantalla y audio"</string> <string name="screenrecord_taps_label" msgid="1595690528298857649">"Mostrar toques en la pantalla"</string> @@ -395,7 +395,7 @@ <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Conectado (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string> <string name="quick_settings_connecting" msgid="2381969772953268809">"Conectando..."</string> <string name="quick_settings_tethering_label" msgid="5257299852322475780">"Compartir conexión"</string> - <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Zona Wi-Fi"</string> + <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punto de acceso"</string> <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activando…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ahorro de datos activado"</string> <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976"> @@ -660,7 +660,7 @@ <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Ajustes rápidos, <xliff:g id="TITLE">%s</xliff:g>."</string> - <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Zona Wi-Fi"</string> + <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Punto de acceso"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string> <string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string> @@ -710,11 +710,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminado"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Burbuja"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración y se muestra más abajo en la sección de conversaciones"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración, y se muestra más abajo en la sección de conversaciones"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Es posible que suene o vibre según los ajustes del teléfono"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Es posible que suene o vibre según los ajustes del teléfono. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Llama tu atención con un acceso directo flotante a este contenido."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones en forma de burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones, como burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ajustes"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string> <string name="no_shortcut" msgid="8257177117568230126">"No se pueden usar funciones de conversación con <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Superior 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Superior 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla inferior completa"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toca dos veces para cambiarla."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dos veces para añadirlo."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Quitar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Añadir <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar icono"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir icono al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover icono"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir icono"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Es posible que la aplicación no funcione con la pantalla dividida."</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index ec91fcddf1da..7e12f724028b 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Ülemine: 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Ülemine: 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Alumine täisekraan"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Asend <xliff:g id="POSITION">%1$d</xliff:g>, paan <xliff:g id="TILE_NAME">%2$s</xliff:g>. Topeltpuudutage muutmiseks."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Topeltpuudutage lisamiseks."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> teisaldamine"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> eemaldamine"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Lisage <xliff:g id="TILE_NAME">%1$s</xliff:g> asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Teisaldage <xliff:g id="TILE_NAME">%1$s</xliff:g> asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"paani lõppu lisamiseks"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisa paan"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 9598ddddb8f7..d5fd753be3f0 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Ezarri goialdea % 50en"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Ezarri goialdea % 30en"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ezarri behealdea pantaila osoan"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. posizioa, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Editatzeko, sakatu birritan."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gehitzeko, sakatu birritan."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mugitu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Kendu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Gehitu <xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>garren postuan"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Eraman <xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>garren postura"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"gehitu lauza amaieran"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mugitu lauza"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Gehitu lauza"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 703d5ec070eb..4827ace9503f 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -800,7 +800,7 @@ <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"برگشت"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"اعلانها"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"میانبرهای صفحهکلید"</string> - <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر طرحبندی صفحهکلید"</string> + <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر جانمایی صفحهکلید"</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"برنامهها"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"دستیار"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"مرورگر"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"٪۵۰ بالا"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"٪۳۰ بالا"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"تمامصفحه پایین"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>. برای ویرایش دو ضربه سریع بزنید."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. برای افزودن دو ضربه سریع بزنید."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"حذف <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"افزودن <xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"افزودن کاشی به انتها"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"افزودن کاشی"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ممکن است برنامه با تقسیم صفحه کار نکند."</string> @@ -979,7 +980,7 @@ <string name="slice_permission_text_2" msgid="6758906940360746983">"- میتواند در <xliff:g id="APP">%1$s</xliff:g> اقدام انجام دهد"</string> <string name="slice_permission_checkbox" msgid="4242888137592298523">"به <xliff:g id="APP">%1$s</xliff:g> اجازه داده شود تکههایی از برنامهها نشان دهد"</string> <string name="slice_permission_allow" msgid="6340449521277951123">"مجاز"</string> - <string name="slice_permission_deny" msgid="6870256451658176895">"رد کردن"</string> + <string name="slice_permission_deny" msgid="6870256451658176895">"مجاز نبودن"</string> <string name="auto_saver_title" msgid="6873691178754086596">"برای زمانبندی «بهینهسازی باتری» ضربه بزنید"</string> <string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 72e16203b4c7..7b8cad3b7a8a 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -379,7 +379,7 @@ <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi on käytössä"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Ei Wi-Fi-verkkoja käytettävissä"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Otetaan käyttöön…"</string> - <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön suoratoisto"</string> + <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön striimaus"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"Lähetetään"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Nimetön laite"</string> <string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Valmis lähetystä varten"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Yläosa 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Yläosa 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Alaosa koko näytölle"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Muokkaa kaksoisnapauttamalla."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lisää kaksoisnapauttamalla."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Poista <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g> kohtaan <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g> kohtaan <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lisää kiekko loppuun"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisää kiekko"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Sovellus ei ehkä toimi jaetulla näytöllä."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 91bb30f8dc3d..3290830b5c79 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % dans le haut"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % dans le haut"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Plein écran dans le bas"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Touchez deux fois pour modifier."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Touchez deux fois pour ajouter."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Supprimer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ajouter <xliff:g id="TILE_NAME">%1$s</xliff:g> à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g> à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"retirer la tuile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la tuile à la fin"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter la tuile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 59b9cfc8a949..50442b283e21 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Écran du haut à 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Écran du haut à 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Écran du bas en plein écran"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, \"<xliff:g id="TILE_NAME">%2$s</xliff:g>\". Appuyer deux fois pour modifier."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Appuyer deux fois pour ajouter."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Déplacer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Supprimer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ajouter l\'élément \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Déplacer l\'élément \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer la carte"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la carte à la fin"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la carte"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter une carte"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index fe50a97943ae..42831c3a5aee 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -710,11 +710,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Configuración predeterminada"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Burbulla"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Non soa nin vibra, e aparece máis abaixo na sección de conversas"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podería soar ou vibrar en función da configuración do teléfono"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sen son nin vibración, e aparecen máis abaixo na sección de conversas"</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Poderían soar ou vibrar en función da configuración do teléfono"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Podería soar ou vibrar en función da configuración do teléfono. Conversas desde a burbulla da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantén a túa atención cun atallo flotante a este contido."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstrase na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstranse na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non admite funcións de conversa"</string> @@ -784,7 +784,7 @@ <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Deter"</string> <string name="keyboard_key_media_next" msgid="8502476691227914952">"Seguinte"</string> <string name="keyboard_key_media_previous" msgid="5637875709190955351">"Anterior"</string> - <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Rebobinar"</string> + <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Retroceder"</string> <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Avance rápido"</string> <string name="keyboard_key_page_up" msgid="173914303254199845">"Re Páx"</string> <string name="keyboard_key_page_down" msgid="9035902490071829731">"Av Páx"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % arriba"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % arriba"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla completa abaixo"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toca dúas veces o elemento para editalo."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dúas veces o elemento para engadilo"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Elimina <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Engadir \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarxeta"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engadir tarxeta ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover tarxeta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engadir tarxeta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Pode que a aplicación non funcione coa pantalla dividida."</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 1e58b04c6dfb..5971c4d442a7 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -857,7 +857,7 @@ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ટાઇલને ફરીથી ગોઠવવા માટે આંગળી દબાવીને ખેંચો"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"દૂર કરવા માટે અહીં ખેંચો"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"તમને ઓછામાં ઓછી <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ટાઇલની જરૂર છે"</string> - <string name="qs_edit" msgid="5583565172803472437">"સંપાદિત કરો"</string> + <string name="qs_edit" msgid="5583565172803472437">"ફેરફાર કરો"</string> <string name="tuner_time" msgid="2450785840990529997">"સમય"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"કલાક, મિનિટ અને સેકન્ડ બતાવો"</item> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"શીર્ષ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"શીર્ષ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"તળિયાની પૂર્ણ સ્ક્રીન"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"સ્થિતિ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. સંપાદિત કરવા માટે બે વાર ટૅપ કરો."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ઉમેરવા માટે બે વાર ટૅપ કરો."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ખસેડો"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> દૂર કરો"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> જગ્યા પર <xliff:g id="TILE_NAME">%1$s</xliff:g>ને ઉમેરો"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> જગ્યા પર <xliff:g id="TILE_NAME">%1$s</xliff:g>ને ખસેડો"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ટાઇલને અંતે ઉમેરો"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ટાઇલ ઉમેરો"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"વિભાજિત-સ્ક્રીન સાથે ઍપ્લિકેશન કદાચ કામ ન કરે."</string> diff --git a/packages/SystemUI/res/values-h740dp-port/dimens.xml b/packages/SystemUI/res/values-h740dp-port/dimens.xml new file mode 100644 index 000000000000..966066ffe56b --- /dev/null +++ b/packages/SystemUI/res/values-h740dp-port/dimens.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<resources> + <dimen name="qs_tile_height">106dp</dimen> + <dimen name="qs_tile_margin_vertical">24dp</dimen> + + <!-- The height of the qs customize header. Should be + (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) - + (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp)) + --> + <dimen name="qs_customize_header_min_height">46dp</dimen> + <dimen name="qs_tile_margin_top">18dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 24f2e6dfdd78..3cdf0a36474d 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -512,7 +512,7 @@ <string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string> <string name="notification_section_header_gentle" msgid="6804099527336337197">"बिना आवाज़ किए मिलने वाली सूचनाएं"</string> - <string name="notification_section_header_alerting" msgid="5581175033680477651">"वाइब्रेशन या आवाज़ के साथ मिलने वाली सूचनाएं"</string> + <string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"बातचीत"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string> @@ -712,11 +712,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"बबल"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और \'बातचीत\', सेक्शन में सबसे नीचे दिखती है"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और बातचीत, सेक्शन में सबसे नीचे दिखती है"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है. <xliff:g id="APP_NAME">%1$s</xliff:g> में होने वाली बातचीत, डिफ़ॉल्ट रूप से बबल के तौर पर दिखती है."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ़्लोट करने वाले शॉर्टकट की मदद से इस सामग्री पर आपका ध्यान बना रहता है."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत की सुविधा, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिंग"</string> <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर बातचीत की सुविधाएं काम नहीं करतीं"</string> @@ -884,12 +884,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ऊपर की स्क्रीन को 50% बनाएं"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ऊपर की स्क्रीन को 30% बनाएं"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"नीचे की स्क्रीन को फ़ुल स्क्रीन बनाएं"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. में बदलाव करने के लिए दो बार छूएं."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. जोड़ने के लिए दो बार छूएं."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> को ले जाएं"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> निकालें"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काे क्रम संख्या <xliff:g id="POSITION">%2$d</xliff:g> पर जाेड़ें"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काे क्रम संख्या <xliff:g id="POSITION">%2$d</xliff:g> पर ले जाएं"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाएं"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल को आखिरी पोज़िशन पर जोड़ें"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल को किसी और पोज़िशन पर ले जाएं"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोड़ें"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"टाइल की पोज़िशन <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"हो सकता है कि ऐप्लिकेशन विभाजित स्क्रीन के साथ काम ना करे."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 3ebaef95112d..0b7a6dd64640 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -887,12 +887,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gornji zaslon na 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gornji zaslon na 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Donji zaslon u cijeli zaslon"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dodirnite dvaput da biste uredili."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dodirnite dvaput da biste dodali."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ukloni <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodajte pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premjestite pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premještanje kartice"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index f01ac1da64bd..79301784c270 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -428,7 +428,7 @@ <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string> <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Képernyő rögzítése"</string> - <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Kezdés"</string> + <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string> <string name="media_seamless_remote_device" msgid="177033467332920464">"Eszköz"</string> <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Váltás az alkalmazások között felfelé csúsztatással"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Felső 50%-ra"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Felső 30%-ra"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Alsó teljes képernyőre"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozíció: <xliff:g id="TILE_NAME">%2$s</xliff:g>. Koppintson duplán a szerkesztéshez."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Koppintson duplán a hozzáadáshoz."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> eltávolítása"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> hozzáadása a következő pozícióhoz: <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése a következő pozícióba: <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"mozaiknak a végéhez való hozzáadásához"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Mozaik hozzáadása"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 65f4325e515e..214a6d710bbc 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -511,7 +511,7 @@ <string name="notification_section_header_incoming" msgid="850925217908095197">"Նոր"</string> <string name="notification_section_header_gentle" msgid="6804099527336337197">"Անձայն"</string> <string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string> - <string name="notification_section_header_conversations" msgid="821834744538345661">"Խոսակցություններ"</string> + <string name="notification_section_header_conversations" msgid="821834744538345661">"Զրույցներ"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ջնջել բոլոր անձայն ծանուցումները"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string> <string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Վերևի էկրանը՝ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Վերևի էկրանը՝ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ներքևի էկրանը՝ լիաէկրան"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>: Կրկնակի հպեք՝ փոխելու համար:"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>: Կրկնակի հպեք՝ ավելացնելու համար:"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Տեղափոխել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Հեռացնել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ավելացնել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը դիրք <xliff:g id="POSITION">%2$d</xliff:g>-ում"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը տեղափոխել դիրք <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկը վերջում"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ավելացնել սալիկ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում:"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index ea0dd97c5b37..97652f0c6ae6 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -854,7 +854,7 @@ <string name="left_icon" msgid="5036278531966897006">"Ikon kiri"</string> <string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk mengatur ulang kartu"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Atas 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Atas 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Layar penuh di bawah"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Ketuk dua kali untuk mengedit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ketuk dua kali untuk menambahkan."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Hapus <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Tambahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"menambahkan kartu ke akhir"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan kartu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index f185c193c952..4a0856c1c669 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -853,10 +853,10 @@ <string name="right_keycode" msgid="2480715509844798438">"Lykiltákn til hægri"</string> <string name="left_icon" msgid="5036278531966897006">"Tákn til vinstri"</string> <string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við reitum"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string> - <string name="drag_to_remove_disabled" msgid="933046987838658850">"Reitirnir mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> + <string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Breyta"</string> <string name="tuner_time" msgid="2450785840990529997">"Tími"</string> <string-array name="clock_options"> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Efri 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Efri 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Neðri á öllum skjánum"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Staða <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Ýttu tvisvar til að breyta."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ýttu tvisvar til að bæta við."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Fjarlægja <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Bæta <xliff:g id="TILE_NAME">%1$s</xliff:g> við í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g> í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta flís við aftast"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta flís við"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Hugsanlega virkar forritið ekki ef skjánum er skipt upp."</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 6ee4415387b1..e5e76386f38b 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Può suonare o vibrare in base alle impostazioni del telefono"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Può suonare o vibrare in base alle impostazioni del telefono. Conversazioni dalla bolla <xliff:g id="APP_NAME">%1$s</xliff:g> per impostazione predefinita."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantiene la tua attenzione con una scorciatoia mobile a questi contenuti."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Viene mostrata in cima alla sezione delle conversazioni, appare sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Appare in cima alla sezione delle conversazioni e sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Impostazioni"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorità"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non supporta le funzionalità delle conversazioni"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Schermata superiore al 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Schermata superiore al 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Schermata inferiore a schermo intero"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tocca due volte per modificare."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tocca due volte per aggiungere."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Sposta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Rimuovi <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Aggiungi il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> alla posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Sposta il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> nella posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"aggiungere il riquadro alla fine"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Aggiungi riquadro"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"L\'app potrebbe non funzionare con lo schermo diviso."</string> @@ -987,7 +988,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Il Risparmio energetico verrà attivato automaticamente quando la carica della batteria sarà inferiore a <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Impostazioni"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"Esegui dump heap SysUI"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 29411a6e531f..fc586355b0c1 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"עליון 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"עליון 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"מסך תחתון מלא"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. הקש פעמיים כדי לערוך."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. הקש פעמיים כדי להוסיף."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"הזזת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"הסרת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"הוספת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"העברת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת האריח"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח בסוף הרשימה"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"העברת האריח"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"הוספת אריח"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ייתכן שהיישום לא יפעל עם מסך מפוצל."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index a5a92b85630f..2ad7ea73d84e 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"上 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"上 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"下部全画面"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> の <xliff:g id="TILE_NAME">%2$s</xliff:g> を編集するにはダブルタップします。"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を追加するにはダブルタップします。"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を移動します"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を削除します"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に追加"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に移動"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"タイルを削除"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"タイルを最後に追加"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"タイルを移動"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"タイルを追加"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"アプリは分割画面では動作しないことがあります。"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 0fbabcd9b43d..9ea94240369c 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"იპყრობს თქვენს ყურადღებას ამ კონტენტის მოლივლივე მალსახმობით."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების სექციის ზედა ნაწილში მოლივლივე ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების ზედა ნაწილში ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"პარამეტრები"</string> <string name="notification_priority_title" msgid="2079708866333537093">"პრიორიტეტი"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს არ აქვს მიმოწერის ფუნქციების მხარდაჭერა"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ზედა ეკრანი — 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ზედა ეკრანი — 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ქვედა ნაწილის სრულ ეკრანზე გაშლა"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. რედაქტირებისთვის, შეეხეთ ორმაგად."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. დასამატებლად, შეეხეთ ორმაგად."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის წაშლა"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის დამატება პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ფილის ბოლოში დამატება"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"მოზაიკის დამატება"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 0906c7163d69..e9bdb11bb746 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -28,7 +28,7 @@ <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды"</string> <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Пайдалану барысына байланысты <xliff:g id="PERCENTAGE">%1$s</xliff:g> заряд, шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string> <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> заряд, шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string> - <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Battery Saver қосулы."</string> + <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Батареяны үнемдеу режимі қосулы."</string> <string name="invalid_charger" msgid="4370074072117767416">"USB арқылы зарядтау мүмкін емес. Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз."</string> <string name="invalid_charger_title" msgid="938685362320735167">"USB арқылы зарядтау мүмкін емес"</string> <string name="invalid_charger_text" msgid="2339310107232691577">"Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз"</string> @@ -419,7 +419,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> дейін"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Қараңғы тақырып"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Батареяны үнемдеу режимі"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Күн батқанда қосу"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string> @@ -710,13 +710,13 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Әдепкі"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Көпіршік"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс не діріл қолданылмайды"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, төменде әңгімелер бөлімінде шығады"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, әңгімелер бөлімінің төмен жағында шығады"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін. <xliff:g id="APP_NAME">%1$s</xliff:g> чаттары әдепкісінше қалқымалы етіп көрсетіледі."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Осы мазмұнға бекітілген қалқымалы таңбашамен назарыңызды өзіне тартады."</string> <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Әңгімелер бөлімінің жоғарғы жағында тұрады, қалқыма хабар түрінде шығады, құлыптаулы экранда профиль суретін көрсетеді"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Параметрлер"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Маңыздылығы"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Маңызды"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> әңгімелесу функцияларын қолдамайды."</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Жақындағы қалқыма хабарлар жоқ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string> @@ -766,7 +766,7 @@ </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Батареяны пайдалану"</string> <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнемдеу режимі"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Өнімділікті және фондық деректерді азайтады"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> @@ -857,7 +857,7 @@ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string> - <string name="qs_edit" msgid="5583565172803472437">"Өңдеу"</string> + <string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string> <string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% жоғарғы жақта"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% жоғарғы жақта"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Төменгісін толық экранға шығару"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> орны, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Өңдеу үшін екі рет түртіңіз."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Қосу үшін екі рет түртіңіз."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жылжыту"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жою"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> бөлшегін <xliff:g id="POSITION">%2$d</xliff:g>-позицияға енгізу"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> бөлшегін <xliff:g id="POSITION">%2$d</xliff:g>-позицияға жылжыту"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"бөлшекті соңына қосу"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Бөлшек қосу"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Қолданба бөлінген экранда жұмыс істемеуі мүмкін."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 5bf8f48104db..93a17d285fd4 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -419,7 +419,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"រហូតដល់ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"រចនាប័ទ្មងងឹត"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"កម្មវិធីសន្សំថ្ម"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"មុខងារសន្សំថ្ម"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"បើកនៅពេលថ្ងៃលិច"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"រហូតដល់ពេលថ្ងៃរះ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> @@ -766,7 +766,7 @@ </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"ការប្រើប្រាស់ថ្ម"</string> <string name="battery_detail_charging_summary" msgid="8821202155297559706">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"កម្មវិធីសន្សំថ្ម"</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"មុខងារសន្សំថ្ម"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"កាត់បន្ថយប្រតិបត្តិការ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ខាងលើ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ខាងលើ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"អេក្រង់ពេញខាងក្រោម"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ទីតាំង <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>, ប៉ះពីរដងដើម្បីកែ"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>, ប៉ះពីរដងដើម្បីបន្ថែម"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"យក <xliff:g id="TILE_NAME">%1$s</xliff:g> ចេញ"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"បញ្ចូល <xliff:g id="TILE_NAME">%1$s</xliff:g> ទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g> ទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"បញ្ចូលប្រអប់ទៅខាងចុង"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទីប្រអប់"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"បញ្ចូលប្រអប់"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"កម្មវិធីអាចនឹងមិនដំណើរការនៅលើអេក្រង់បំបែកទេ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 044ecda5230e..b223a3f43587 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"ಸ್ವಯಂ"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಇನ್ವರ್ಟ್ ಮಾಡಿ"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ ಮೋಡ್"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% ಮೇಲಕ್ಕೆ"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% ಮೇಲಕ್ಕೆ"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ಸ್ಥಳ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ಎಡಿಟ್ ಮಾಡಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ಸೇರಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಸರಿಸಿ"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ತೆಗೆದುಹಾಕಿ"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="POSITION">%2$d</xliff:g> ಗೆ ಸೇರಿಸಿ"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="POSITION">%2$d</xliff:g> ಗೆ ಸರಿಸಿ"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ಕೊನೆಯಲ್ಲಿ ಟೈಲ್ ಸೇರಿಸಿ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ಟೈಲ್ ಸೇರಿಸಿ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಎಡಿಟರ್."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ವಿಭಜಿಸಿದ ಪರದೆಯಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 30ed951177c2..36ab96c2abeb 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -371,7 +371,7 @@ <string name="quick_settings_time_label" msgid="3352680970557509303">"시간"</string> <string name="quick_settings_user_label" msgid="1253515509432672496">"나"</string> <string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string> - <string name="quick_settings_user_new_user" msgid="3347905871336069666">"새 사용자"</string> + <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string> @@ -473,7 +473,7 @@ <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string> <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> - <string name="user_new_user_name" msgid="2019166282704195789">"새 사용자"</string> + <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string> @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있음"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있습니다. 기본적으로 <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 대화창으로 표시됩니다."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"이 콘텐츠로 연결되는 플로팅 바로가기로 사용자의 주의를 끕니다."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됩니다."</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됨"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"설정"</string> <string name="notification_priority_title" msgid="2079708866333537093">"우선순위"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 대화 기능을 지원하지 않습니다."</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"위쪽 화면 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"위쪽 화면 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"아래쪽 화면 전체화면"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"위치 <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. 수정하려면 두 번 탭하세요."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. 추가하려면 두 번 탭하세요."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 이동"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 삭제"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일을 위치 <xliff:g id="POSITION">%2$d</xliff:g>에 추가"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일을 위치 <xliff:g id="POSITION">%2$d</xliff:g>(으)로 이동"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"끝에 타일 추가"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"타일 추가"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 8eab2f19ec17..a420f715f286 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер түрүндө көрүнөт."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Калкыма ыкчам баскыч менен көңүлүңүздү бул мазмунга буруп турат."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуу бөлүмүнүн жогорку жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуулар тизмесинин өйдө жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Жөндөөлөр"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> жазышуу функцияларын колдоого албайт"</string> @@ -854,7 +854,7 @@ <string name="left_icon" msgid="5036278531966897006">"¨Солго¨ сүрөтчөсү"</string> <string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн, кармап туруп, сүйрөңүз"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Алып салуу үчүн бул жерге сүйрөңүз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string> <string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Үстүнкү экранды 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Үстүнкү экранды 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ылдыйкы экранды толук экран режимине өткөрүү"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Орду - <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Түзөтүү үчүн эки жолу таптаңыз."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Кошуу үчүн эки жолу таптаңыз."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди жылдыруу"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди алып салуу"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> плиткасы <xliff:g id="POSITION">%2$d</xliff:g>-позицияга кошулсун"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> плиткасы <xliff:g id="POSITION">%2$d</xliff:g>-позицияга кошулсун"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ыкчам баскычты өчүрүү"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ыкчам баскычты аягына кошуу"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ыкчам баскычты жылдыруу"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ыкчам баскыч кошуу"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Колдонмодо экран бөлүнбөшү мүмкүн."</string> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 9e1b66f07758..b584dfee1e60 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -47,4 +47,15 @@ <dimen name="global_actions_power_dialog_item_height">130dp</dimen> <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen> + + <dimen name="controls_management_top_padding">12dp</dimen> + <dimen name="controls_management_titles_margin">8dp</dimen> + <dimen name="controls_management_indicator_top_margin">8dp</dimen> + <dimen name="controls_management_list_margin">4dp</dimen> + <dimen name="controls_management_footer_height">56dp</dimen> + <dimen name="controls_management_zone_top_margin">24dp</dimen> + + <!-- (footer_height -48dp)/2 --> + <dimen name="controls_management_footer_top_margin">4dp</dimen> + <dimen name="controls_management_favorites_top_margin">8dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 14cd2113da6d..0f7e2ca8e4c9 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ເທິງສຸດ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ເທິງສຸດ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ເຕັມໜ້າຈໍລຸ່ມສຸດ"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອແກ້ໄຂ."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອເພີ່ມ."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"ລຶບ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"ເພີ່ມ <xliff:g id="TILE_NAME">%1$s</xliff:g> ໄປຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g> ໄປຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ເພີ່ມແຜ່ນໃສ່ທ້າຍ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ເພີ່ມແຜ່ນ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index f8f1dc03905c..27cf8ec33591 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -776,7 +776,7 @@ </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Akum. energ. vartoj."</string> <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"Akumuliat. taus. pr."</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"Akum. taus. pr."</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Sumažinamas našumas ir foninių duomenų naudojimas"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string> @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Viršutinis ekranas 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Viršutinis ekranas 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Apatinis ekranas viso ekrano režimu"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> padėtis, išklotinės elementas „<xliff:g id="TILE_NAME">%2$s</xliff:g>“. Dukart palieskite, kad redaguotumėte."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Išklotinės elementas „<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Dukart palieskite, kad pridėtumėte."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Pašalinti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Pridėti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ į <xliff:g id="POSITION">%2$d</xliff:g> padėtį"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ į <xliff:g id="POSITION">%2$d</xliff:g> padėtį"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridėtumėte išklotinės elementą gale"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridėti išklotinės elementą"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Programa gali neveikti naudojant skaidytą ekraną."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index ea1bc77db679..8af578d8110d 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -713,7 +713,7 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Noklusējums"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Burbulis"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaņas signāla vai vibrācijas"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas zemāk sarunu sadaļā"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas tālāk sarunu sadaļā"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Piesaista jūsu uzmanību, rādot peldošu saīsni uz šo saturu."</string> @@ -887,12 +887,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Augšdaļa 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Augšdaļa 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Apakšdaļu pa visu ekrānu"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozīcija, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Lai rediģētu, veiciet dubultskārienu."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lai pievienotu, veiciet dubultskārienu."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pārvietot elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Noņemt elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Pievienot elementu “<xliff:g id="TILE_NAME">%1$s</xliff:g>” <xliff:g id="POSITION">%2$d</xliff:g>. pozīcijā"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Pārvietot elementu “<xliff:g id="TILE_NAME">%1$s</xliff:g>” uz <xliff:g id="POSITION">%2$d</xliff:g>. pozīciju"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pievienot elementu beigās"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pievienot elementu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Iespējams, lietotnē nedarbosies ekrāna sadalīšana."</string> diff --git a/packages/SystemUI/res/values-mcc310-mnc004/strings.xml b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml new file mode 100644 index 000000000000..f8ed0c01fa83 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] --> + <string name="data_connection_5g_plus" translatable="false">5G UW</string> +</resources> diff --git a/packages/SystemUI/res/values-mcc311-mnc480/strings.xml b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml new file mode 100644 index 000000000000..f8ed0c01fa83 --- /dev/null +++ b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] --> + <string name="data_connection_5g_plus" translatable="false">5G UW</string> +</resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 9ec598a7090f..c55e532ecfec 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -710,13 +710,13 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Стандардно"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Балонче"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува под делот за разговор"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да ѕвони или вибрира во зависност од поставките на телефонот"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да ѕвони или вибрира во зависност од поставките на телефонот Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Ви го задржува вниманието со лебдечка кратенка на содржинава."</string> <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Се појавува на горниот дел од секцијата на разговорот во вид на лебдечко меурче, покажувајќи ја профилната слика на заклучениот екран"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Поставки"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддржува функции за разговор"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Нема неодамнешни балончиња"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Горниот 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Горниот 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Долниот на цел екран"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Место <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Допрете двапати за уредување."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Допрете двапати за додавање."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Отстранете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Додајте <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицијата <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицијата <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додавање на плочката на крај"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додавање плочка"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Апликацијата можеби нема да работи во поделен екран."</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 277169e2f630..b6b878e0bc9e 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്തിട്ടില്ല"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"യാന്ത്രികം"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നിറം മാറ്റുക"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നെഗറ്റീവ് ലുക്ക്"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"വർണ്ണം ശരിയാക്കൽ മോഡ്"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string> <string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"മുകളിൽ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"മുകളിൽ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"താഴെ പൂർണ്ണ സ്ക്രീൻ"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. എഡിറ്റുചെയ്യുന്നതിന് രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ചേർക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കംചെയ്യുക"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> സ്ഥാനത്തേയ്ക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> ചേർക്കുക"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> സ്ഥാനത്തേയ്ക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ടൈൽ നീക്കം ചെയ്യുക"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ടൈൽ, അവസാന ഭാഗത്ത് ചേർക്കുക"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ടൈൽ ചേർക്കുക"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> എന്നതിലേക്ക് നീക്കുക"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> എന്ന സ്ഥാനത്തേക്ക് ചേർക്കുക"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"സ്പ്ലിറ്റ്-സ്ക്രീനിനൊപ്പം ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 49421f6d3ea8..0a0fbd7a8081 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -710,7 +710,7 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Өгөгдмөл"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Бөмбөлөг"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсвэл чичиргээ байхгүй"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харицан ярианы хэсгийн доод талд харагдана"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харилцан ярианы хэсгийн доод талд харагдана"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй. <xliff:g id="APP_NAME">%1$s</xliff:g>-н харилцан яриаг өгөгдмөл тохиргооны дагуу бөмбөлөг болгоно."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Энэ контентын хөвөн гарч ирэх товчлолтойгоор таны анхаарлыг татдаг."</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Дээд 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Дээд 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Доод бүтэн дэлгэц"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Байршил <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Засахын тулд 2 удаа дарна уу."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Нэмэхийн тулд 2 удаа дарна уу."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г зөөх"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г устгах"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г <xliff:g id="POSITION">%2$d</xliff:g> байрлалд нэмэх"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г байрлал <xliff:g id="POSITION">%2$d</xliff:g> руу зөөх"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дуусгахын тулд хавтан нэмэх"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Хавтан нэмэх"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Апп хуваагдсан дэлгэцэд ажиллахгүй."</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 6bbf77f781f0..5a4f64ecb1d4 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"शीर्ष 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"शीर्ष 10"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"तळाशी फुल स्क्रीन"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिती <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. संपादित करण्यासाठी दोनदा टॅप करा."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> . जोडण्यासाठी दोनदा टॅप करा."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> हलवा"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काढा"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर जोडा"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर हलवा"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल शेवटच्या स्थानावर जोडा"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोडा"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"अॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index b16427624cc6..ee558d41808b 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Atas 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Atas 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Skrin penuh bawah"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dwiketik untuk mengedit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dwiketik untuk menambah."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Alih keluar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Tambahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> pada kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alih keluar jubin"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tambahkan jubin pada bahagian hujung"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Alihkan jubin"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan jubin"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Apl mungkin tidak berfungsi dengan skrin pisah."</string> @@ -987,7 +988,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Penjimat Bateri akan dihidupkan secara automatik setelah kuasa bateri kurang daripada <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Tetapan"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"Longgok Tmbunn SysUI"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda."</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 6d71f1d5fcdd..73f5b03d3e9d 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"အပေါ်ဘက် မျက်နှာပြင် ၅၀%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"အပေါ်ဘက် မျက်နှာပြင် ၃၀%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"အောက်ခြေ မျက်နှာပြင်အပြည့်"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>၊ <xliff:g id="TILE_NAME">%2$s</xliff:g> နေရာ။ တည်းဖြတ်ရန် နှစ်ချက်တို့ပါ။"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>။ ပေါင်းထည့်ရန် နှစ်ချက်တို့ပါ။"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုရွှေ့ပါ"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုဖယ်ရှားပါ"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို အနေအထား <xliff:g id="POSITION">%2$d</xliff:g> သို့ ပေါင်းထည့်ရန်"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို အနေအထား <xliff:g id="POSITION">%2$d</xliff:g> သို့ ရွှေ့ရန်"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"အဆုံးတွင် အကွက်ငယ်ထည့်ရန်"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"အကွက်ငယ်ကို ထည့်ရန်"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"မျက်နှာပြင် ခွဲခြမ်းပြသမှုဖြင့် အက်ပ်သည် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 14de00ae2c85..745e3d8f114f 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Sett størrelsen på den øverste delen av skjermen til 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Sett størrelsen på den øverste delen av skjermen til 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Utvid den nederste delen av skjermen til hele skjermen"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Plassering <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dobbelttrykk for å endre."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dobbelttrykk for å legge til."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Legg til <xliff:g id="TILE_NAME">%1$s</xliff:g> i posisjon <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g> til posisjon <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"legge til en infobrikke på slutten"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Legg til en infobrikke"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Det kan hende at appen ikke fungerer med delt skjerm."</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index f003d270549c..8eecf9b882db 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -43,7 +43,7 @@ <string name="status_bar_settings_mute_label" msgid="914392730086057522">"म्युट गर्नुहोस्"</string> <string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"स्वतः"</string> <string name="status_bar_settings_notifications" msgid="5285316949980621438">"सूचनाहरू"</string> - <string name="bluetooth_tethered" msgid="4171071193052799041">"ब्लुटुथ टेथर भयो"</string> + <string name="bluetooth_tethered" msgid="4171071193052799041">"ब्लुटुथ टेदर भयो"</string> <string name="status_bar_input_method_settings_configure_input_methods" msgid="2972273031043777851">"इनपुट विधिहरू सेटअप गर्नुहोस्"</string> <string name="status_bar_use_physical_keyboard" msgid="4849251850931213371">"वास्तविक किबोर्ड"</string> <string name="usb_device_permission_prompt" msgid="4414719028369181772">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> माथि पहुँच राख्ने अनुमति दिने हो?"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"माथिल्लो भाग ५०%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"माथिल्लो भाग ३०%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"तल्लो भाग फुल स्क्रिन"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। सम्पादन गर्नाका लागि डबल ट्याप गर्नुहोस्।"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। थप्नका लागि डबल ट्याप गर्नुहोस्।"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई सार्नुहोस्"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई हटाउनुहोस्"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई <xliff:g id="POSITION">%2$d</xliff:g> स्थितिमा थप्नुहोस्"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई <xliff:g id="POSITION">%2$d</xliff:g> स्थितिमा सार्नुहोस्"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल अन्त्यमा हाल्नुहोस्"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल हाल्नुहोस्"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"अनुप्रयोगले विभाजित-स्क्रिनमा काम नगर्न सक्छ।"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 679be33181ba..1c233455f9f0 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -390,7 +390,7 @@ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Kleuren omkeren"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"Modus voor kleurcorrectie"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellingen"</string> - <string name="quick_settings_done" msgid="2163641301648855793">"Gereed"</string> + <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string> <string name="quick_settings_connected" msgid="3873605509184830379">"Verbonden"</string> <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Verbonden, batterij <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="quick_settings_connecting" msgid="2381969772953268809">"Verbinding maken…"</string> @@ -692,7 +692,7 @@ <string name="notification_channel_silenced" msgid="1995937493874511359">"Deze meldingen worden zonder geluid weergegeven"</string> <string name="notification_channel_unsilenced" msgid="94878840742161152">"Deze meldingen stellen je op de hoogte"</string> <string name="inline_blocking_helper" msgid="2891486013649543452">"Meestal sluit je deze meldingen. \nWil je ze blijven weergeven?"</string> - <string name="inline_done_button" msgid="6043094985588909584">"Gereed"</string> + <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string> <string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"Deze meldingen blijven weergeven?"</string> <string name="inline_stop_button" msgid="2453460935438696090">"Meldingen stoppen"</string> @@ -710,11 +710,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Bubbel"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt op een lagere positie in het gedeelte met gesprekken weergegeven"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken weergegeven"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Trekt de aandacht met een zwevende snelkoppeling naar deze content."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gedeelte met gesprekken weergegeven, verschijnt als zwevende bubbel, geeft de profielfoto weer op het vergrendelscherm"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gespreksgedeelte weergegeven, verschijnt als zwevende bubbel, geeft profielfoto weer op vergrendelscherm"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Instellingen"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string> @@ -739,7 +739,7 @@ <string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Meldingen van dit kanaal toestaan"</string> <string name="notification_more_settings" msgid="4936228656989201793">"Meer instellingen"</string> <string name="notification_app_settings" msgid="8963648463858039377">"Aanpassen"</string> - <string name="notification_done" msgid="6215117625922713976">"Gereed"</string> + <string name="notification_done" msgid="6215117625922713976">"Klaar"</string> <string name="inline_undo" msgid="9026953267645116526">"Ongedaan maken"</string> <string name="demote" msgid="6225813324237153980">"Deze melding markeren als geen gesprek"</string> <string name="notification_conversation_favorite" msgid="1905240206975921907">"Belangrijk gesprek"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Bovenste scherm 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Bovenste scherm 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Onderste scherm op volledig scherm"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Positie <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dubbeltik om te bewerken."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om toe te voegen."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verwijderen"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> toevoegen aan positie <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen naar positie <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"tegel verwijderen"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tegel toevoegen aan einde"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Tegel verplaatsen"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tegel toevoegen"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"App werkt mogelijk niet met gesplitst scherm."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 8cacc97aa79b..33f170912df2 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ। <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଡିଫଲ୍ଟ ଭାବରେ ବବଲ୍ ହୁଏ।"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ଏହି ବିଷୟବସ୍ତୁ ପାଇଁ ଏକ ଭାସମାନ ସର୍ଟକଟ୍ ସହ ଆପଣଙ୍କର ଧ୍ୟାନ ଦିଅନ୍ତୁ।"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଭାସମାନ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଫ୍ଲୋଟିଂ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string> @@ -853,7 +853,7 @@ <string name="right_keycode" msgid="2480715509844798438">"ଡାହାଣ କୀ\'କୋଡ୍"</string> <string name="left_icon" msgid="5036278531966897006">"ବାମ ଆଇକନ୍"</string> <string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଡ଼ିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ୍ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ଉପର ଆଡ଼କୁ 50% କରନ୍ତୁ"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ଉପର ଆଡ଼କୁ 30% କରନ୍ତୁ"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ତଳ ଅଂଶର ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ଅବସ୍ଥାନ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ଏଡିଟ୍ କରିବାକୁ ଡବଲ୍-ଟାପ୍ କରନ୍ତୁ।"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ଯୋଡ଼ିବା ପାଇଁ ଡବଲ୍-ଟାପ୍ କରନ୍ତୁ।"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ନିଅନ୍ତୁ"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ବାହାର କରିଦିଅନ୍ତୁ"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> ଅବସ୍ଥାନକୁ <xliff:g id="TILE_NAME">%1$s</xliff:g> ଯୋଡନ୍ତୁ"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> ଅବସ୍ଥାନକୁ <xliff:g id="TILE_NAME">%1$s</xliff:g> ଘୁଞ୍ଚାନ୍ତୁ"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ଶେଷରେ ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରୀନରେ ଆପ୍ କାମ କରିନପାରେ।"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index a3bc4e0644ae..8ee374072f94 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ਉੱਪਰ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ਉੱਪਰ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ਹੇਠਾਂ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ਸਥਿਤੀ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ਸੰਪਾਦਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ ਤਬਦੀਲ ਕਰੋ"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਹਟਾਓ"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਾਨ \'ਤੇ ਲਿਜਾਓ"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ਟਾਇਲ ਨੂੰ ਅੰਤ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 443c8f15f400..6e1c99655647 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% górnej części ekranu"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% górnej części ekranu"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Dolna część ekranu na pełnym ekranie"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Położenie <xliff:g id="POSITION">%1$d</xliff:g>, kafelek <xliff:g id="TILE_NAME">%2$s</xliff:g>. Kliknij dwukrotnie, by edytować."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>. Kliknij dwukrotnie, by dodać."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Usuń kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodaj kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> w położeniu <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> w położenie <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodać kartę na końcu"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodaj kartę"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacja może nie działać przy podzielonym ekranie."</string> @@ -1021,7 +1022,7 @@ <string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Przenieś w lewy dolny róg"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Przenieś w prawy dolny róg"</string> <string name="bubble_dismiss_text" msgid="1314082410868930066">"Zamknij dymek"</string> - <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymku"</string> + <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymka"</string> <string name="bubbles_user_education_title" msgid="5547017089271445797">"Czatuj, korzystając z dymków"</string> <string name="bubbles_user_education_description" msgid="1160281719576715211">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string> <string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Zarządzaj dymkami w dowolnym momencie"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 6918ad8ffa09..33b6a1670350 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -711,12 +711,12 @@ <string name="notification_bubble_title" msgid="8330481035191903164">"Bolha"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podem vibrar ou tocar com base nas configurações do smartphone"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparecem na parte superior de uma seção de conversa, em forma de balões, mostrando a foto do perfil na tela de bloqueio"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string> <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Parte superior a 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Parte superior a 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Parte inferior em tela cheia"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adicionar <xliff:g id="TILE_NAME">%1$s</xliff:g> à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"É possível que o app não funcione com o recurso de divisão de tela."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 907199217827..9c69703c2934 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode tocar ou vibrar com base nas definições do telemóvel."</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode tocar ou vibrar com base nas definições do telemóvel. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém a sua atenção com um atalho flutuante para este conteúdo."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior da secção de conversas, surge como um balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece no topo da secção de conversas, surge como balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Definições"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> <string name="no_shortcut" msgid="8257177117568230126">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> não suporta funcionalidades de conversa."</string> @@ -853,7 +853,7 @@ <string name="right_keycode" msgid="2480715509844798438">"Código de tecla direito"</string> <string name="left_icon" msgid="5036278531966897006">"Ícone esquerdo"</string> <string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para adicionar mosaicos."</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tocar sem soltar e arrastar para adicionar mosaicos"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tocar sem soltar e arrastar para reorganizar os mosaicos"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% no ecrã superior"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% no ecrã superior"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ecrã inferior inteiro"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adicionar <xliff:g id="TILE_NAME">%1$s</xliff:g> à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o cartão"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o cartão ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover cartão"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar cartão"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"A app pode não funcionar com o ecrã dividido."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 6918ad8ffa09..33b6a1670350 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -711,12 +711,12 @@ <string name="notification_bubble_title" msgid="8330481035191903164">"Bolha"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podem vibrar ou tocar com base nas configurações do smartphone"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparecem na parte superior de uma seção de conversa, em forma de balões, mostrando a foto do perfil na tela de bloqueio"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string> <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Parte superior a 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Parte superior a 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Parte inferior em tela cheia"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adicionar <xliff:g id="TILE_NAME">%1$s</xliff:g> à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"É possível que o app não funcione com o recurso de divisão de tela."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 12a622c8d90f..0abfc56ccac8 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -887,12 +887,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Partea de sus: 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Partea de sus: 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Partea de jos pe ecran complet"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Atingeți de două ori pentru a edita."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Atingeți de două ori pentru a adăuga."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Eliminați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adăugați <xliff:g id="TILE_NAME">%1$s</xliff:g> pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g> pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"eliminați cardul"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adăugați cardul la sfârșit"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mutați cardul"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adăugați un card"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 117e2b28c95a..27ed4ea6714c 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -720,7 +720,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Звонок или вибрация в зависимости от настроек телефона"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Звонок или вибрация в зависимости от настроек телефона. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Привлекает ваше внимание к контенту с помощью плавающего ярлыка"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, а также показывает фото профиля на заблокированном экране"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, фото профиля показывается на заблок. экране"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string> @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Верхний на 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Верхний на 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Нижний во весь экран"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>, кнопка \"<xliff:g id="TILE_NAME">%2$s</xliff:g>\". Чтобы изменить, нажмите дважды."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\". Чтобы добавить, нажмите дважды."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Переместить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Удалить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Добавить значок <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Переместить значок <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить панель"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить панель в конец"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить панель"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить панель"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Приложение не поддерживает разделение экрана."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 2be74df2dc47..8ec42dcb4a6d 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ඉහළම 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ඉහළම 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"පහළ පූර්ණ තිරය"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. වෙනස් කිරීමට දෙවරක් තට්ටු කරන්න."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. එක් කිරීමට දෙවරක් තට්ටු කරන්න."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ගෙන යන්න"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ඉවත් කරන්න"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> තත්ත්වයට <xliff:g id="POSITION">%2$d</xliff:g> එක් කරන්න"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> තත්ත්වයට <xliff:g id="POSITION">%2$d</xliff:g> ගෙන යන්න"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"අගට ටයිල් එක් කරන්න"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ටයිල් එක් කරන්න"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"යෙදුම බෙදුම්-තිරය සමග ක්රියා නොකළ හැකිය."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 001b4740e264..94bff94c71e3 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Horná – 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Horná – 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Dolná – na celú obrazovku"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozícia <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Upravíte ju dvojitým klepnutím."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Pridáte ju dvojitým klepnutím."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Presunúť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Odstrániť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Pridať <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Presunúť <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridať kartu na koniec"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridať kartu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikácia nemusí fungovať so zapnutou rozdelenou obrazovkou."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 937bb79de3c8..f45c673849d3 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -95,7 +95,7 @@ <string name="screenrecord_description" msgid="1123231719680353736">"Med snemanjem lahko sistem Android zajame morebitne občutljive podatke, ki so prikazani na zaslonu ali se predvajajo v napravi. To vključuje gesla, podatke za plačilo, fotografije, sporočila in zvok."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Snemanje zvoka"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvok v napravi"</string> - <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvok v napravi, kot so glasba, klici in toni zvonjenja"</string> + <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvoki v napravi, kot so glasba, klici in toni zvonjenja"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Mikrofon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Zvok v napravi in mikrofon"</string> <string name="screenrecord_start" msgid="330991441575775004">"Začni"</string> @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Zgornji 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Zgornji 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Spodnji v celozaslonski način"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Če želite urediti, se dvakrat dotaknite."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Če želite dodati, se dvakrat dotaknite."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premik tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Odstranitev tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodaj ploščico <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premakni ploščico <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodajanje ploščice na konec"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajanje ploščice"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 97fb0323c947..a67130bb7d3a 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -480,7 +480,7 @@ <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> - <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo!"</string> + <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo"</string> <string name="guest_notification_title" msgid="4434456703930764167">"Përdorues vizitor"</string> <string name="guest_notification_text" msgid="4202692942089571351">"Për të fshirë aplikacionet dhe të dhënat, hiqe përdoruesin vizitor"</string> <string name="guest_notification_remove_action" msgid="4153019027696868099">"HIQ VIZITORIN"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Lart 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Lart 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ekrani i plotë poshtë"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Trokit dy herë për ta redaktuar."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Trokit dy herë për ta shtuar."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Zhvendose <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Hiqe <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Shto <xliff:g id="TILE_NAME">%1$s</xliff:g> te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Zhvendos <xliff:g id="TILE_NAME">%1$s</xliff:g> te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"hiq pllakëzën"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"shto pllakëzën në fund"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Shto pllakëzën"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 083e45efc026..b2a4cead129a 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -887,12 +887,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Горњи екран 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Горњи екран 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Режим целог екрана за доњи екран"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Двапут додирните да бисте изменили."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двапут додирните да бисте додали."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Премести плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Уклони плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Додајте „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позицију <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместите „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позицију <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додали плочицу на крај"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додајте плочицу"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Апликација можда неће функционисати са подељеним екраном."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index e2e6b7d31359..3e9310e5dec6 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Övre 50 %"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Övre 30 %"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Helskärm på nedre skärm"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryck snabbt två gånger för att redigera positionen."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lägg till genom att trycka snabbt två gånger."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ta bort <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Lägg till <xliff:g id="TILE_NAME">%1$s</xliff:g> på position <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g> till position <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ta bort ruta"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lägg till ruta i slutet"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytta ruta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lägg till ruta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Appen kanske inte fungerar med delad skärm."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 0abb58fdd15f..f5f9c246bab1 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Juu 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Juu 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Skrini nzima ya chini"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gusa mara mbili ili ubadilishe."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gusa mara mbili ili uongeze."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ondoa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ongeza <xliff:g id="TILE_NAME">%1$s</xliff:g> kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Sogeza <xliff:g id="TILE_NAME">%1$s</xliff:g> kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ongeza kigae mwishoni"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ongeza kigae"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index f98e103ed9c4..3d77bd7ae01c 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"மேலே 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"மேலே 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"கீழ்ப்புறம் முழுத் திரை"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"நிலை <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. திருத்த, இருமுறை தட்டவும்."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. சேர்க்க, இருமுறை தட்டவும்."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தவும்"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ அகற்றவும்"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"நிலைப்பாடு <xliff:g id="POSITION">%2$d</xliff:g> இல் <xliff:g id="TILE_NAME">%1$s</xliff:g>ஐச் சேர்க்கும்"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"நிலைப்பாடு <xliff:g id="POSITION">%2$d</xliff:g>க்கு <xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தும்"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"டைலை அகற்றும்"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"கடைசியில் டைலைச் சேர்க்கும்"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"டைலை நகர்த்து"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"டைலைச் சேர்"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> இடத்திற்கு நகர்த்தும்"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> என்ற இடத்தில் சேர்க்கும்"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"இடம்: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"திரைப் பிரிப்பில் ஆப்ஸ் வேலைசெய்யாமல் போகக்கூடும்."</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index c9d211cf2b86..6c0c2c4a8a75 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -693,7 +693,7 @@ <string name="notification_channel_unsilenced" msgid="94878840742161152">"ఈ నోటిఫికేషన్లు మిమ్మల్ని హెచ్చరిస్తాయి"</string> <string name="inline_blocking_helper" msgid="2891486013649543452">"మీరు సాధారణంగా ఈ నోటిఫికేషన్లను విస్మరిస్తారు. \nవాటి ప్రదర్శనను కొనసాగించాలా?"</string> <string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string> - <string name="inline_ok_button" msgid="603075490581280343">"అప్లై చేయి"</string> + <string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"ఈ నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string> <string name="inline_stop_button" msgid="2453460935438696090">"నోటిఫికేషన్లను ఆపివేయి"</string> <string name="inline_deliver_silently_button" msgid="2714314213321223286">"నిశ్శబ్దంగా బట్వాడా చేయండి"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ఎగువ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ఎగువ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"దిగువ పూర్తి స్క్రీన్"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. సవరించడానికి రెండుసార్లు నొక్కండి."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. జోడించడానికి రెండుసార్లు నొక్కండి."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలిస్తుంది"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తీసివేస్తుంది"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"స్థానం <xliff:g id="POSITION">%2$d</xliff:g>కి <xliff:g id="TILE_NAME">%1$s</xliff:g>ని జోడించండి"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"స్థానం <xliff:g id="POSITION">%2$d</xliff:g>కి <xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలించండి"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్ను తీసివేయండి"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ముగించడానికి టైల్ను జోడించండి"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్ను తరలించండి"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"టైల్ను జోడించండి"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్ల ఎడిటర్."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"స్క్రీన్ విభజనతో యాప్ పని చేయకపోవచ్చు."</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 7705be92c5e0..6ea105f8d3f9 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"เริ่มบันทึกเลยไหม"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนที่ปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string> + <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนซึ่งปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"บันทึกเสียง"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"เสียงจากอุปกรณ์"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"เสียงจากอุปกรณ์ เช่น เพลง การโทร และเสียงเรียกเข้า"</string> @@ -337,7 +337,7 @@ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"ขณะนี้หน้าจอล็อกอยู่ในแนวนอน"</string> <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"ขณะนี้หน้าจอล็อกอยู่ในแนวตั้ง"</string> <string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string> - <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาหน้าจอ"</string> + <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาจอภาพ"</string> <string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string> <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"แตะไอคอนค้างไว้เพื่อดูตัวเลือกอื่นๆ"</string> <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string> @@ -509,7 +509,7 @@ <string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"ใหม่"</string> - <string name="notification_section_header_gentle" msgid="6804099527336337197">"เงียบ"</string> + <string name="notification_section_header_gentle" msgid="6804099527336337197">"ปิดเสียง"</string> <string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"การสนทนา"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ล้างการแจ้งเตือนแบบไม่มีเสียงทั้งหมด"</string> @@ -706,7 +706,7 @@ <string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"แจ้งเตือนต่อไป"</string> <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ปิดการแจ้งเตือน"</string> <string name="inline_keep_showing_app" msgid="4393429060390649757">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string> - <string name="notification_silence_title" msgid="8608090968400832335">"เงียบ"</string> + <string name="notification_silence_title" msgid="8608090968400832335">"ปิดเสียง"</string> <string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"บับเบิล"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"ไม่มีเสียงหรือการสั่น"</string> @@ -854,7 +854,7 @@ <string name="left_icon" msgid="5036278531966897006">"ไอคอนทางซ้าย"</string> <string name="right_icon" msgid="1103955040645237425">"ไอคอนทางขวา"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"กดค้างแล้วลากเพื่อเพิ่มการ์ด"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดการ์ดค้างไว้แล้วลากเพื่อจัดเรียงใหม่"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดค้างแล้วลากเพื่อจัดเรียงการ์ดใหม่"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ลากมาที่นี่เพื่อนำออก"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"คุณต้องมีการ์ดอย่างน้อย <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> รายการ"</string> <string name="qs_edit" msgid="5583565172803472437">"แก้ไข"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ด้านบน 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ด้านบน 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"เต็มหน้าจอด้านล่าง"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g> <xliff:g id="TILE_NAME">%2$s</xliff:g> แตะ 2 ครั้งเพื่อแก้ไข"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> แตะ 2 ครั้งเพื่อเพิ่ม"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"นำ <xliff:g id="TILE_NAME">%1$s</xliff:g> ออก"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"เพิ่ม <xliff:g id="TILE_NAME">%1$s</xliff:g> ไปยังตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g> ไปยังตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"เพิ่มชิ้นส่วนต่อท้าย"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"เพิ่มชิ้นส่วน"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"แอปอาจใช้ไม่ได้กับโหมดแยกหน้าจอ"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index c37c1116de9e..fa9dd92d1788 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gawing 50% ang nasa itaas"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gawing 30% ang nasa itaas"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"I-full screen ang nasa ibaba"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. I-double tap upang i-edit."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. I-double tap upang idagdag."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Alisin ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Idagdag ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyong <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyong <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alisin ang tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"idagdag ang tile sa dulo"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ilipat ang tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Magdagdag ng tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Maaaring hindi gumana ang app sa split-screen."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 108f00c41be6..da5db750dd89 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Üstte %50"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Üstte %30"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Altta tam ekran"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. konum, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Düzenlemek için iki kez dokunun."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Eklemek için iki kez dokunun."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu taşı"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu kaldır"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> öğesini <xliff:g id="POSITION">%2$d</xliff:g> konumuna ekle"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> öğesini <xliff:g id="POSITION">%2$d</xliff:g> konumuna taşı"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Karoyu kaldırmak için"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Sona karo eklemek için"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Karoyu taşı"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Karo ekle"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Uygulama bölünmüş ekranda çalışmayabilir."</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 90a512f3ecbe..5c4f55da156d 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -892,12 +892,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Верхнє вікно на 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Верхнє вікно на 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Нижнє вікно на весь екран"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Двічі торкніться, щоб змінити."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двічі торкніться, щоб додати."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Видалити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Додати <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицію <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицію <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити опцію"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати опцію в кінець"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити опцію"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати опцію"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Додаток може не працювати в режимі розділеного екрана."</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 8a69b1cfb3dc..70e313d2a248 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"اوپر %50"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"اوپر %30"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"نچلی فل اسکرین"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>۔ ترمیم کرنے کیلئے دو بار تھپتھپائیں۔"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>۔ شامل کرنے کیلئے دو بار تھپتھپائیں۔"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو منتقل کریں"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ہٹائیں"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"پوزیشن <xliff:g id="TILE_NAME">%1$s</xliff:g> میں <xliff:g id="POSITION">%2$d</xliff:g> شامل کریں"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو پوزیشن <xliff:g id="POSITION">%2$d</xliff:g> میں منتقل کریں"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ختم کرنے کے لیے ٹائل شامل کریں"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ٹائل شامل کریں"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"ممکن ہے کہ ایپ سپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 47dd75c55b8d..c234642f161c 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Tepada 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Tepada 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pastda to‘liq ekran"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>-joy, “<xliff:g id="TILE_NAME">%2$s</xliff:g>” tugmasi. Tahrirlash uchun ustiga ikki marta bosing."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi. Qo‘shish uchun ustiga ikki marta bosing."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasini ko‘chirish"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> tugmasini olib tashlash"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g>-joyga buni qo‘shish: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g>-joyga buni ko‘chirish: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"oxiriga katakcha kiritish"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Katakcha kiritish"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Ilova ekranni ikkiga bo‘lish rejimini qo‘llab-quvvatlamaydi."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 344e292f1bd5..b70d70d3b531 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Trên 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Trên 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Toàn màn hình phía dưới"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Nhấn đúp để chỉnh sửa."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Nhấn đúp để thêm."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Xóa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Thêm <xliff:g id="TILE_NAME">%1$s</xliff:g> vào vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g> đến vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"thêm ô vào cuối"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Thêm ô"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 2a720d8f715f..c5fe4ddaae96 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"顶部 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"顶部 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"底部全屏"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。点按两次即可修改。"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。点按两次即可添加。"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移动<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"移除<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"将“<xliff:g id="TILE_NAME">%1$s</xliff:g>”添加到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"将“<xliff:g id="TILE_NAME">%1$s</xliff:g>”移动到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除图块"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"将图块添加到末尾"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动图块"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"添加图块"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"应用可能无法在分屏模式下正常运行。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 88e3fb7f967b..9ec6775215f4 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"可能會根據手機設定發出鈴聲或震動"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機設定發出鈴聲或震動。「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會預設以對話氣泡顯示。"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"為此內容建立浮動捷徑以保持注意力。"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"在對話部分的頂部以浮動對話氣泡顯示,並在上鎖畫面顯示個人檔案相片"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"以浮動對話泡顯示在對話部分的頂部,並在上鎖畫面顯示個人檔案相片"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"設定"</string> <string name="notification_priority_title" msgid="2079708866333537093">"重要"</string> <string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"頂部 50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"頂部 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"底部全螢幕"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。輕按兩下即可編輯。"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕按兩下即可新增。"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"將「<xliff:g id="TILE_NAME">%1$s</xliff:g>」加去位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"將「<xliff:g id="TILE_NAME">%1$s</xliff:g>」移去位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到最尾"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"加圖塊"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"應用程式可能無法在分割畫面中運作。"</string> @@ -987,7 +988,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式將會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"知道了"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"轉儲 SysUI 堆"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index ad5e370b9c66..828e86f9b70b 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -710,7 +710,7 @@ <string name="notification_alert_title" msgid="3656229781017543655">"預設"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"泡泡"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"不震動或發出聲音"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並顯示在對話部分的下方"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並調整排序到其他對話下方"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"根據手機的設定響鈴或震動"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"利用浮動式捷徑快速存取這項內容。"</string> @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"以 50% 的螢幕空間顯示頂端畫面"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"以 30% 的螢幕空間顯示頂端畫面"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"以全螢幕顯示底部畫面"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。輕觸兩下即可編輯。"</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕觸兩下即可新增。"</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 新增到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 移到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾處"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"新增圖塊"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"應用程式可能無法在分割畫面中運作。"</string> @@ -987,7 +988,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"我知道了"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"傾印 SysUI 記憶體快照"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 7303e142f929..719a6d003773 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -882,12 +882,13 @@ <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Okuphezulu okungu-50%"</string> <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Okuphezulu okungu-30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ngaphansi kwesikrini esigcwele"</string> - <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Isimo esingu-<xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Thepha kabili ukuze uhlele."</string> - <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Thepha kabili ukuze ungeze."</string> - <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Susa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Engeza i-<xliff:g id="TILE_NAME">%1$s</xliff:g> ukuze ubeke i-<xliff:g id="POSITION">%2$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g> ukuze ubeke i-<xliff:g id="POSITION">%2$d</xliff:g>"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engeza ithayela ekugcineni"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engeza ithayela"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="4689301323912928801">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0d40a9fa26d4..f07627a1a346 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -482,20 +482,21 @@ <!-- The size of the gesture span needed to activate the "pull" notification expansion --> <dimen name="pull_span_min">25dp</dimen> - <dimen name="qs_tile_height">106dp</dimen> + <dimen name="qs_tile_height">96dp</dimen> <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 --> <dimen name="qs_tile_layout_margin_side">18dp</dimen> <dimen name="qs_tile_margin_horizontal">18dp</dimen> <dimen name="qs_tile_margin_horizontal_two_line">2dp</dimen> - <dimen name="qs_tile_margin_vertical">24dp</dimen> + <dimen name="qs_tile_margin_vertical">2dp</dimen> <dimen name="qs_tile_margin_top_bottom">12dp</dimen> <dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen> <!-- The height of the qs customize header. Should be - (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) - + (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (0dp)) - (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp)) --> - <dimen name="qs_customize_header_min_height">46dp</dimen> - <dimen name="qs_tile_margin_top">18dp</dimen> + <dimen name="qs_customize_header_min_height">28dp</dimen> + <dimen name="qs_tile_margin_top">0dp</dimen> + <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen> <dimen name="qs_tile_background_size">44dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> @@ -1295,6 +1296,8 @@ <dimen name="qs_footer_horizontal_margin">22dp</dimen> <dimen name="qs_media_disabled_seekbar_height">1dp</dimen> <dimen name="qs_media_enabled_seekbar_height">3dp</dimen> + <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen> + <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen> <dimen name="magnification_border_size">5dp</dimen> <dimen name="magnification_frame_move_short">5dp</dimen> @@ -1350,15 +1353,19 @@ <dimen name="controls_management_side_padding">16dp</dimen> <dimen name="controls_management_titles_margin">16dp</dimen> <dimen name="controls_management_footer_side_margin">8dp</dimen> + <dimen name="controls_management_footer_top_margin">@dimen/controls_management_footer_side_margin</dimen> <dimen name="controls_management_list_margin">16dp</dimen> + <dimen name="controls_management_indicator_top_margin">@dimen/controls_management_list_margin</dimen> <dimen name="controls_management_apps_list_margin">64dp</dimen> <dimen name="controls_management_editing_list_margin">48dp</dimen> <dimen name="controls_management_editing_divider_margin">24dp</dimen> <dimen name="controls_management_apps_extra_side_margin">8dp</dimen> <dimen name="controls_management_zone_top_margin">32dp</dimen> + <dimen name="controls_management_favorites_top_margin">@dimen/controls_management_zone_top_margin</dimen> <dimen name="controls_management_status_side_margin">16dp</dimen> <dimen name="controls_management_page_indicator_height">24dp</dimen> <dimen name="controls_management_checkbox_size">25dp</dimen> + <dimen name="controls_management_footer_height">72dp</dimen> <dimen name="controls_title_size">24sp</dimen> <dimen name="controls_subtitle_size">16sp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index a56f6f56836a..b8e8db5ecf50 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -177,6 +177,9 @@ <item type="id" name="accessibility_action_controls_move_before" /> <item type="id" name="accessibility_action_controls_move_after" /> + <item type="id" name="accessibility_action_qs_move_to_position" /> + <item type="id" name="accessibility_action_qs_add_to_position" /> + <!-- Accessibility actions for PIP --> <item type="id" name="action_pip_resize" /> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 38501eb8da3d..77ce39fe9953 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2267,23 +2267,26 @@ <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] --> <string name="accessibility_action_divider_bottom_full">Bottom full screen</string> - <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string> + <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_remove_tile_action">remove tile</string> - <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_add_tile_label"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g>. Double tap to add.</string> + <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_add_action">add tile to end</string> - <!-- Accessibility description of option to move QS tile [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_move_tile">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string> + <!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_start_move">Move tile</string> - <!-- Accessibility description of option to remove QS tile [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_remove_tile">Remove <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string> + <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_start_add">Add tile</string> - <!-- Accessibility action when QS tile is to be added [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_tile_add">Add <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string> + <!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string> - <!-- Accessibility action when QS tile is to be moved [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_tile_move">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string> + <!-- Accessibility description when QS tile is to be added, indicating the destination position [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_add_to_position">Add to position <xliff:g id="position" example="5">%1$d</xliff:g></string> + + <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string> <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] --> <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string> diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java index 5f8815665d88..2deeb1230f09 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java @@ -39,15 +39,21 @@ import javax.inject.Singleton; */ @Singleton public class ForegroundServiceController { - public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW}; + public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA, + AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION}; private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>(); private final Object mMutex = new Object(); + private final NotificationEntryManager mEntryManager; private final Handler mMainHandler; @Inject - public ForegroundServiceController(AppOpsController appOpsController, - @Main Handler mainHandler) { + public ForegroundServiceController(NotificationEntryManager entryManager, + AppOpsController appOpsController, @Main Handler mainHandler) { + mEntryManager = entryManager; mMainHandler = mainHandler; appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> { mMainHandler.post(() -> { @@ -81,6 +87,19 @@ public class ForegroundServiceController { } /** + * Returns the keys for notifications from this package using the standard template, + * if they exist. + */ + @Nullable + public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) { + synchronized (mMutex) { + final ForegroundServicesUserState services = mUserServices.get(userId); + if (services == null) return null; + return services.getStandardLayoutKeys(pkg); + } + } + + /** * Gets active app ops for this user and package */ @Nullable @@ -121,6 +140,31 @@ public class ForegroundServiceController { userServices.removeOp(packageName, appOpCode); } } + + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // AppOpsCoordinator + // Update appOps if there are associated pending or visible notifications + final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName); + if (notificationKeys != null) { + boolean changed = false; + for (String key : notificationKeys) { + final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key); + if (entry != null + && uid == entry.getSbn().getUid() + && packageName.equals(entry.getSbn().getPackageName())) { + synchronized (entry.mActiveAppOps) { + if (active) { + changed |= entry.mActiveAppOps.add(appOpCode); + } else { + changed |= entry.mActiveAppOps.remove(appOpCode); + } + } + } + } + if (changed) { + mEntryManager.updateNotifications("appOpChanged pkg=" + packageName); + } + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java index 1515272569d6..bb445832da93 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java @@ -172,8 +172,24 @@ public class ForegroundServiceNotificationListener { sbn.getPackageName(), sbn.getKey()); } } + tagAppOps(entry); return true; }, true /* create if not found */); } + + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // AppOpsCoordinator + private void tagAppOps(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); + ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( + sbn.getUserId(), + sbn.getPackageName()); + synchronized (entry.mActiveAppOps) { + entry.mActiveAppOps.clear(); + if (activeOps != null) { + entry.mActiveAppOps.addAll(activeOps); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 64b35caeeef2..2b9514f6d23f 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,8 +16,13 @@ package com.android.systemui.appops; +import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; + import android.app.AppOpsManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.location.LocationManager; import android.media.AudioManager; @@ -35,6 +40,7 @@ import androidx.annotation.WorkerThread; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; @@ -54,7 +60,7 @@ import javax.inject.Singleton; * NotificationPresenter to be displayed to the user. */ @Singleton -public class AppOpsControllerImpl implements AppOpsController, +public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController, AppOpsManager.OnOpActiveChangedInternalListener, AppOpsManager.OnOpNotedListener, Dumpable { @@ -65,6 +71,7 @@ public class AppOpsControllerImpl implements AppOpsController, private static final String TAG = "AppOpsControllerImpl"; private static final boolean DEBUG = false; + private final BroadcastDispatcher mDispatcher; private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private final LocationManager mLocationManager; @@ -79,6 +86,7 @@ public class AppOpsControllerImpl implements AppOpsController, private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); private final PermissionFlagsCache mFlagsCache; private boolean mListening; + private boolean mMicMuted; @GuardedBy("mActiveItems") private final List<AppOpItem> mActiveItems = new ArrayList<>(); @@ -104,8 +112,10 @@ public class AppOpsControllerImpl implements AppOpsController, @Background Looper bgLooper, DumpManager dumpManager, PermissionFlagsCache cache, - AudioManager audioManager + AudioManager audioManager, + BroadcastDispatcher dispatcher ) { + mDispatcher = dispatcher; mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mFlagsCache = cache; mBGHandler = new H(bgLooper); @@ -114,6 +124,7 @@ public class AppOpsControllerImpl implements AppOpsController, mCallbacksByCode.put(OPS[i], new ArraySet<>()); } mAudioManager = audioManager; + mMicMuted = audioManager.isMicrophoneMute(); mLocationManager = context.getSystemService(LocationManager.class); dumpManager.registerDumpable(TAG, this); } @@ -132,6 +143,8 @@ public class AppOpsControllerImpl implements AppOpsController, mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); + mDispatcher.registerReceiverWithHandler(this, + new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler); } else { mAppOps.stopWatchingActive(this); @@ -139,6 +152,7 @@ public class AppOpsControllerImpl implements AppOpsController, mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); mBGHandler.removeCallbacksAndMessages(null); // null removes all + mDispatcher.unregisterReceiver(this); synchronized (mActiveItems) { mActiveItems.clear(); mRecordingsByUid.clear(); @@ -466,6 +480,9 @@ public class AppOpsControllerImpl implements AppOpsController, } private boolean isAnyRecordingPausedLocked(int uid) { + if (mMicMuted) { + return true; + } List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid); if (configs == null) return false; int configsNum = configs.size(); @@ -520,6 +537,12 @@ public class AppOpsControllerImpl implements AppOpsController, } }; + @Override + public void onReceive(Context context, Intent intent) { + mMicMuted = mAudioManager.isMicrophoneMute(); + updateRecordingPausedStatus(); + } + protected class H extends Handler { H(Looper looper) { super(looper); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 8e49d584788a..008571099a64 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -26,6 +26,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ResolveInfo; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Handler; import android.provider.Settings; @@ -66,8 +68,10 @@ import dagger.Lazy; @Singleton final class AssistHandleReminderExpBehavior implements BehaviorController { - private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed"; - private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count"; + private static final Uri LEARNING_TIME_ELAPSED_URI = + Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS); + private static final Uri LEARNING_EVENT_COUNT_URI = + Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT); private static final String LEARNED_HINT_LAST_SHOWN_KEY = "reminder_exp_learned_hint_last_shown"; private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(10); @@ -181,6 +185,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { private boolean mIsNavBarHidden; private boolean mIsLauncherShowing; private int mConsecutiveTaskSwitches; + @Nullable private ContentObserver mSettingObserver; /** Whether user has learned the gesture. */ private boolean mIsLearned; @@ -248,9 +253,22 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver); mLearningTimeElapsed = Settings.Secure.getLong( - context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0); + context.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + /* default = */ 0); mLearningCount = Settings.Secure.getInt( - context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0); + context.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + /* default = */ 0); + mSettingObserver = new SettingsObserver(context, mHandler); + context.getContentResolver().registerContentObserver( + LEARNING_TIME_ELAPSED_URI, + /* notifyForDescendants = */ true, + mSettingObserver); + context.getContentResolver().registerContentObserver( + LEARNING_EVENT_COUNT_URI, + /* notifyForDescendants = */ true, + mSettingObserver); mLearnedHintLastShownEpochDay = Settings.Secure.getLong( context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0); mLastLearningTimestamp = mClock.currentTimeMillis(); @@ -264,8 +282,20 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { if (mContext != null) { mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver); mBootCompleteCache.get().removeListener(mBootCompleteListener); - Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0); - Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0); + mContext.getContentResolver().unregisterContentObserver(mSettingObserver); + mSettingObserver = null; + // putString to use overrideableByRestore + Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + Long.toString(0L), + /* overrideableByRestore = */ true); + // putString to use overrideableByRestore + Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + Integer.toString(0), + /* overrideableByRestore = */ true); Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0); mContext = null; } @@ -282,8 +312,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { return; } - Settings.Secure.putLong( - mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount); + // putString to use overrideableByRestore + Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + Integer.toString(++mLearningCount), + /* overrideableByRestore = */ true); } @Override @@ -460,8 +494,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mIsLearned = mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs(); - mHandler.post(() -> Settings.Secure.putLong( - mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed)); + // putString to use overrideableByRestore + mHandler.post(() -> Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + Long.toString(mLearningTimeElapsed), + /* overrideableByRestore = */ true)); } private void resetConsecutiveTaskSwitches() { @@ -589,4 +627,32 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { + "=" + getShowWhenTaught()); } + + private final class SettingsObserver extends ContentObserver { + + private final Context mContext; + + SettingsObserver(Context context, Handler handler) { + super(handler); + mContext = context; + } + + @Override + public void onChange(boolean selfChange, @Nullable Uri uri) { + if (LEARNING_TIME_ELAPSED_URI.equals(uri)) { + mLastLearningTimestamp = mClock.currentTimeMillis(); + mLearningTimeElapsed = Settings.Secure.getLong( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + /* default = */ 0); + } else if (LEARNING_EVENT_COUNT_URI.equals(uri)) { + mLearningCount = Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + /* default = */ 0); + } + + super.onChange(selfChange, uri); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index c6d128631930..b71e3adae8ac 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -286,6 +286,15 @@ class Bubble implements BubbleViewProvider { } /** + * Sets whether this bubble is considered visually interruptive. Normally pulled from the + * {@link NotificationEntry}, this method is purely for testing. + */ + @VisibleForTesting + void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) { + mIsVisuallyInterruptive = visuallyInterruptive; + } + + /** * Starts a task to inflate & load any necessary information to display a bubble. * * @param callback the callback to notify one the bubble is ready to be displayed. @@ -411,6 +420,7 @@ class Bubble implements BubbleViewProvider { } else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) { // Was an intent bubble now it's a shortcut bubble... still unregister the listener mIntent.unregisterCancelListener(mIntentCancelListener); + mIntentActive = false; mIntent = null; } mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 2948b47103bf..5deae925ba30 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -82,7 +82,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; -import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; @@ -407,7 +406,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (bubble.getBubbleIntent() == null) { return; } - if (bubble.isIntentActive()) { + if (bubble.isIntentActive() + || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { bubble.setPendingIntentCanceled(); return; } @@ -1120,8 +1120,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { notif.setInterruption(); } - Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); - inflateAndAdd(bubble, suppressFlyout, showInShade); + if (!notif.getRanking().visuallyInterruptive() + && (notif.getBubbleMetadata() != null + && !notif.getBubbleMetadata().getAutoExpandBubble()) + && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { + // Update the bubble but don't promote it out of overflow + Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); + b.setEntry(notif); + } else { + Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); + inflateAndAdd(bubble, suppressFlyout, showInShade); + } } void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index d2dc506c8e5c..85ea8bc91484 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -277,7 +277,8 @@ public class BubbleData { } else { // Updates an existing bubble bubble.setSuppressFlyout(suppressFlyout); - doUpdate(bubble); + // If there is no flyout, we probably shouldn't show the bubble at the top + doUpdate(bubble, !suppressFlyout /* reorder */); } if (bubble.shouldAutoExpand()) { @@ -431,12 +432,12 @@ public class BubbleData { } } - private void doUpdate(Bubble bubble) { + private void doUpdate(Bubble bubble, boolean reorder) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "doUpdate: " + bubble); } mStateChange.updatedBubble = bubble; - if (!isExpanded()) { + if (!isExpanded() && reorder) { int prevPos = mBubbles.indexOf(bubble); mBubbles.remove(bubble); mBubbles.add(0, bubble); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java index c1dd8c36ff6f..48a9b91721f5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java @@ -30,10 +30,6 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger * @param e UI event */ public void log(Bubble b, UiEventEnum e) { - if (b.getInstanceId() == null) { - // Added from persistence -- TODO log this with specific event? - return; - } logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId()); } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt index 1bda841d4a63..d930c98cabe1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt @@ -87,6 +87,10 @@ class ControlsFavoritePersistenceWrapper( * @param list a list of favorite controls. The list will be stored in the same order. */ fun storeFavorites(structures: List<StructureInfo>) { + if (structures.isEmpty() && !file.exists()) { + // Do not create a new file to store nothing + return + } executor.execute { Log.d(TAG, "Saving data to file: $file") val atomicFile = AtomicFile(file) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 31830b94e8e4..40662536e57e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -263,6 +263,7 @@ internal class ControlHolder( val context = itemView.context val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) + icon.imageTintList = null ci.customIcon?.let { icon.setImageIcon(it) } ?: run { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5f75c96be128..5a525974f3cb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -104,6 +104,7 @@ class ControlsUiControllerImpl @Inject constructor ( private var hidden = true private lateinit var dismissGlobalActions: Runnable private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow) + private var retainCache = false private val collator = Collator.getInstance(context.resources.configuration.locales[0]) private val localeComparator = compareBy<SelectionItem, CharSequence>(collator) { @@ -149,6 +150,7 @@ class ControlsUiControllerImpl @Inject constructor ( this.parent = parent this.dismissGlobalActions = dismissGlobalActions hidden = false + retainCache = false allStructures = controlsController.get().getFavorites() selectedStructure = loadPreference(allStructures) @@ -235,6 +237,8 @@ class ControlsUiControllerImpl @Inject constructor ( } putIntentExtras(i, si) startActivity(context, i) + + retainCache = true } private fun putIntentExtras(intent: Intent, si: StructureInfo) { @@ -497,7 +501,7 @@ class ControlsUiControllerImpl @Inject constructor ( controlsListingController.get().removeCallback(listingCallback) - RenderInfo.clearCache() + if (!retainCache) RenderInfo.clearCache() } override fun onRefreshState(componentName: ComponentName, controls: List<Control>) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 1e99a7b733e2..e2361d8f415a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -42,6 +42,7 @@ import android.hardware.SensorPrivacyManager; import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.media.MediaRouter2Manager; +import android.media.session.MediaSessionManager; import android.net.ConnectivityManager; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; @@ -219,6 +220,11 @@ public class SystemServicesModule { } @Provides + static MediaSessionManager provideMediaSessionManager(Context context) { + return context.getSystemService(MediaSessionManager.class); + } + + @Provides @Singleton static NetworkScoreManager provideNetworkScoreManager(Context context) { return context.getSystemService(NetworkScoreManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index ae7d82ac4a5e..253a35c55698 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -430,8 +430,12 @@ public class DozeMachine { /** Give the Part a chance to clean itself up. */ default void destroy() {} - /** Alerts that the screenstate is being changed. */ - default void onScreenState(int state) {} + /** + * Alerts that the screenstate is being changed. + * Note: This may be called from within a call to transitionTo, so local DozeState may not + * be accurate nor match with the new displayState. + */ + default void onScreenState(int displayState) {} } /** A wrapper interface for {@link android.service.dreams.DreamService} */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 64cfb4bcd058..a11997b6b845 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -69,7 +69,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi * --ei brightness_bucket 1} */ private int mDebugBrightnessBucket = -1; - private DozeMachine.State mState; @VisibleForTesting public DozeScreenBrightness(Context context, DozeMachine.Service service, @@ -109,7 +108,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { - mState = newState; switch (newState) { case INITIALIZED: case DOZE: @@ -127,10 +125,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Override public void onScreenState(int state) { - if (!mScreenOff - && (mState == DozeMachine.State.DOZE_AOD - || mState == DozeMachine.State.DOZE_AOD_DOCKED) - && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND)) { + if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) { setLightSensorEnabled(true); } else { setLightSensorEnabled(false); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 0554dc80cba5..ff25439a5f9f 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -19,6 +19,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS; import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; @@ -548,7 +549,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (!mDeviceProvisioned && !action.showBeforeProvisioning()) { return false; } - return true; + return action.shouldShow(); } /** @@ -961,6 +962,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @VisibleForTesting class ScreenshotAction extends SinglePressAction implements LongPressAction { + final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons"; + public ScreenshotAction() { super(R.drawable.ic_screenshot, R.string.global_action_screenshot); } @@ -993,6 +996,19 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } @Override + public boolean shouldShow() { + // Include screenshot in power menu for legacy nav because it is not accessible + // through Recents in that mode + return is2ButtonNavigationEnabled(); + } + + boolean is2ButtonNavigationEnabled() { + return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger( + com.android.internal.R.integer.config_navBarInteractionMode); + } + + + @Override public boolean onLongPress() { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) { mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS); @@ -1615,6 +1631,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, * @return */ CharSequence getMessage(); + + default boolean shouldShow() { + return true; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java new file mode 100644 index 000000000000..aca033e99623 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media; + +import android.content.ComponentName; +import android.content.Context; +import android.media.browse.MediaBrowser; +import android.os.Bundle; + +import javax.inject.Inject; + +/** + * Testable wrapper around {@link MediaBrowser} constructor + */ +public class MediaBrowserFactory { + private final Context mContext; + + @Inject + public MediaBrowserFactory(Context context) { + mContext = context; + } + + /** + * Creates a new MediaBrowser + * + * @param serviceComponent + * @param callback + * @param rootHints + * @return + */ + public MediaBrowser create(ComponentName serviceComponent, + MediaBrowser.ConnectionCallback callback, Bundle rootHints) { + return new MediaBrowser(mContext, serviceComponent, callback, rootHints); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 15c60921cca5..d8d9bd7e95b8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -11,6 +11,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -22,6 +23,7 @@ import com.android.systemui.util.Utils import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor +import java.util.TreeMap import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton @@ -41,7 +43,7 @@ class MediaCarouselController @Inject constructor( private val mediaHostStatesManager: MediaHostStatesManager, private val activityStarter: ActivityStarter, @Main executor: DelayableExecutor, - mediaManager: MediaDataFilter, + private val mediaManager: MediaDataManager, configurationController: ConfigurationController, falsingManager: FalsingManager ) { @@ -103,13 +105,12 @@ class MediaCarouselController @Inject constructor( private val mediaCarousel: MediaScrollView private val mediaCarouselScrollHandler: MediaCarouselScrollHandler val mediaFrame: ViewGroup - val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf() private lateinit var settingsButton: View - private val mediaData: MutableMap<String, MediaData> = mutableMapOf() private val mediaContent: ViewGroup private val pageIndicator: PageIndicator private val visualStabilityCallback: VisualStabilityManager.Callback private var needsReordering: Boolean = false + private var keysNeedRemoval = mutableSetOf<String>() private var isRtl: Boolean = false set(value) { if (value != field) { @@ -123,7 +124,7 @@ class MediaCarouselController @Inject constructor( set(value) { if (field != value) { field = value - for (player in mediaPlayers.values) { + for (player in MediaPlayerData.players()) { player.setListening(field) } } @@ -161,6 +162,10 @@ class MediaCarouselController @Inject constructor( needsReordering = false reorderAllPlayers() } + + keysNeedRemoval.forEach { removePlayer(it) } + keysNeedRemoval.clear() + // Let's reset our scroll position mediaCarouselScrollHandler.scrollToStart() } @@ -168,20 +173,23 @@ class MediaCarouselController @Inject constructor( true /* persistent */) mediaManager.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - oldKey?.let { mediaData.remove(it) } - if (!data.active && !Utils.useMediaResumption(context)) { - // This view is inactive, let's remove this! This happens e.g when dismissing / - // timing out a view. We still have the data around because resumption could - // be on, but we should save the resources and release this. - onMediaDataRemoved(key) + addOrUpdatePlayer(key, oldKey, data) + val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active + if (canRemove && !Utils.useMediaResumption(context)) { + // This view isn't playing, let's remove this! This happens e.g when + // dismissing/timing out a view. We still have the data around because + // resumption could be on, but we should save the resources and release this. + if (visualStabilityManager.isReorderingAllowed) { + onMediaDataRemoved(key) + } else { + keysNeedRemoval.add(key) + } } else { - mediaData.put(key, data) - addOrUpdatePlayer(key, oldKey, data) + keysNeedRemoval.remove(key) } } override fun onMediaDataRemoved(key: String) { - mediaData.remove(key) removePlayer(key) } }) @@ -224,53 +232,36 @@ class MediaCarouselController @Inject constructor( } private fun reorderAllPlayers() { - for (mediaPlayer in mediaPlayers.values) { - val view = mediaPlayer.view?.player - if (mediaPlayer.isPlaying && mediaContent.indexOfChild(view) != 0) { - mediaContent.removeView(view) - mediaContent.addView(view, 0) + mediaContent.removeAllViews() + for (mediaPlayer in MediaPlayerData.players()) { + mediaPlayer.view?.let { + mediaContent.addView(it.player) } } mediaCarouselScrollHandler.onPlayersChanged() } private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) { - // If the key was changed, update entry - val oldData = mediaPlayers[oldKey] - if (oldData != null) { - val oldData = mediaPlayers.remove(oldKey) - mediaPlayers.put(key, oldData!!)?.let { - Log.wtf(TAG, "new key $key already exists when migrating from $oldKey") - } - } - var existingPlayer = mediaPlayers[key] + val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey) if (existingPlayer == null) { - existingPlayer = mediaControlPanelFactory.get() - existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), - mediaContent)) - existingPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions - mediaPlayers[key] = existingPlayer + var newPlayer = mediaControlPanelFactory.get() + newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) + newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - existingPlayer.view?.player?.setLayoutParams(lp) - existingPlayer.bind(data) - existingPlayer.setListening(currentlyExpanded) - updatePlayerToState(existingPlayer, noAnimation = true) - if (existingPlayer.isPlaying) { - mediaContent.addView(existingPlayer.view?.player, 0) - } else { - mediaContent.addView(existingPlayer.view?.player) - } + newPlayer.view?.player?.setLayoutParams(lp) + newPlayer.bind(data, key) + newPlayer.setListening(currentlyExpanded) + MediaPlayerData.addMediaPlayer(key, data, newPlayer) + updatePlayerToState(newPlayer, noAnimation = true) + reorderAllPlayers() } else { - existingPlayer.bind(data) - if (existingPlayer.isPlaying && - mediaContent.indexOfChild(existingPlayer.view?.player) != 0) { - if (visualStabilityManager.isReorderingAllowed) { - mediaContent.removeView(existingPlayer.view?.player) - mediaContent.addView(existingPlayer.view?.player, 0) - } else { - needsReordering = true - } + existingPlayer.bind(data, key) + MediaPlayerData.addMediaPlayer(key, data, existingPlayer) + if (visualStabilityManager.isReorderingAllowed) { + reorderAllPlayers() + } else { + needsReordering = true } } updatePageIndicator() @@ -278,29 +269,30 @@ class MediaCarouselController @Inject constructor( mediaCarousel.requiresRemeasuring = true // Check postcondition: mediaContent should have the same number of children as there are // elements in mediaPlayers. - if (mediaPlayers.size != mediaContent.childCount) { + if (MediaPlayerData.players().size != mediaContent.childCount) { Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") } } - private fun removePlayer(key: String) { - val removed = mediaPlayers.remove(key) + private fun removePlayer(key: String, dismissMediaData: Boolean = true) { + val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) mediaContent.removeView(removed.view?.player) removed.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() + + if (dismissMediaData) { + // Inform the media manager of a potentially late dismissal + mediaManager.dismissMediaData(key, 0L) + } } } private fun recreatePlayers() { - // Note that this will scramble the order of players. Actively playing sessions will, at - // least, still be put in the front. If we want to maintain order, then more work is - // needed. - mediaData.forEach { - key, data -> - removePlayer(key) + MediaPlayerData.mediaData().forEach { (key, data) -> + removePlayer(key, dismissMediaData = false) addOrUpdatePlayer(key = key, oldKey = null, data = data) } } @@ -338,7 +330,7 @@ class MediaCarouselController @Inject constructor( currentStartLocation = startLocation currentEndLocation = endLocation currentTransitionProgress = progress - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { updatePlayerToState(mediaPlayer, immediately) } maybeResetSettingsCog() @@ -387,7 +379,7 @@ class MediaCarouselController @Inject constructor( private fun updateCarouselDimensions() { var width = 0 var height = 0 - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { val controller = mediaPlayer.mediaViewController // When transitioning the view to gone, the view gets smaller, but the translation // Doesn't, let's add the translation @@ -449,7 +441,7 @@ class MediaCarouselController @Inject constructor( this.desiredLocation = desiredLocation this.desiredHostState = it currentlyExpanded = it.expansion > 0 - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { if (animate) { mediaPlayer.mediaViewController.animatePendingStateChange( duration = duration, @@ -471,7 +463,7 @@ class MediaCarouselController @Inject constructor( } fun closeGuts() { - mediaPlayers.values.forEach { + MediaPlayerData.players().forEach { it.closeGuts(true) } } @@ -498,3 +490,49 @@ class MediaCarouselController @Inject constructor( } } } + +@VisibleForTesting +internal object MediaPlayerData { + private data class MediaSortKey( + val data: MediaData, + val updateTime: Long = 0 + ) + + private val comparator = + compareByDescending<MediaSortKey> { it.data.isPlaying } + .thenByDescending { it.data.isLocalSession } + .thenByDescending { !it.data.resumption } + .thenByDescending { it.updateTime } + + private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator) + private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf() + + fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) { + removeMediaPlayer(key) + val sortKey = MediaSortKey(data, System.currentTimeMillis()) + mediaData.put(key, sortKey) + mediaPlayers.put(sortKey, player) + } + + fun getMediaPlayer(key: String, oldKey: String?): MediaControlPanel? { + // If the key was changed, update entry + oldKey?.let { + if (it != key) { + mediaData.remove(it)?.let { sortKey -> mediaData.put(key, sortKey) } + } + } + return mediaData.get(key)?.let { mediaPlayers.get(it) } + } + + fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let { mediaPlayers.remove(it) } + + fun mediaData() = mediaData.entries.map { e -> Pair(e.key, e.value.data) } + + fun players() = mediaPlayers.values + + @VisibleForTesting + fun clear() { + mediaData.clear() + mediaPlayers.clear() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index e55678dc986b..810cecca517f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -82,6 +82,7 @@ public class MediaControlPanel { private Context mContext; private PlayerViewHolder mViewHolder; + private String mKey; private MediaViewController mMediaViewController; private MediaSession.Token mToken; private MediaController mController; @@ -206,10 +207,11 @@ public class MediaControlPanel { /** * Bind this view based on the data given */ - public void bind(@NonNull MediaData data) { + public void bind(@NonNull MediaData data, String key) { if (mViewHolder == null) { return; } + mKey = key; MediaSession.Token token = data.getToken(); mBackgroundColor = data.getBackgroundColor(); if (mToken == null || !mToken.equals(token)) { @@ -359,10 +361,10 @@ public class MediaControlPanel { // Dismiss mViewHolder.getDismiss().setOnClickListener(v -> { - if (data.getNotificationKey() != null) { + if (mKey != null) { closeGuts(); mKeyguardDismissUtil.executeWhenUnlocked(() -> { - mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(), + mMediaDataManagerLazy.get().dismissMediaData(mKey, MediaViewController.GUTS_ANIMATION_DURATION + 100); return true; }, /* requiresShadeOpen */ true); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index dafc52ad8025..40a879abde34 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -82,6 +82,10 @@ data class MediaData( */ var resumeAction: Runnable?, /** + * Local or remote playback + */ + var isLocalSession: Boolean = true, + /** * Indicates that this player is a resumption player (ie. It only shows a play actions which * will start the app and start playing). */ @@ -90,7 +94,17 @@ data class MediaData( * Notification key for cancelling a media player after a timeout (when not using resumption.) */ val notificationKey: String? = null, - var hasCheckedForResume: Boolean = false + var hasCheckedForResume: Boolean = false, + + /** + * If apps do not report PlaybackState, set as null to imply 'undetermined' + */ + val isPlaying: Boolean? = null, + + /** + * Set from the notification and used as fallback when PlaybackState cannot be determined + */ + val isClearable: Boolean = true ) /** State of a media action. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index d0642ccf9714..aa3699e9a22b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -17,65 +17,48 @@ package com.android.systemui.media import javax.inject.Inject -import javax.inject.Singleton /** - * Combines updates from [MediaDataManager] with [MediaDeviceManager]. + * Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */ -@Singleton -class MediaDataCombineLatest @Inject constructor( - private val dataSource: MediaDataManager, - private val deviceSource: MediaDeviceManager -) { +class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, + MediaDeviceManager.Listener { + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf() - init { - dataSource.addListener(object : MediaDataManager.Listener { - override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (oldKey != null && oldKey != key && entries.contains(oldKey)) { - entries[key] = data to entries.remove(oldKey)?.second - update(key, oldKey) - } else { - entries[key] = data to entries[key]?.second - update(key, key) - } - } - override fun onMediaDataRemoved(key: String) { - remove(key) - } - }) - deviceSource.addListener(object : MediaDeviceManager.Listener { - override fun onMediaDeviceChanged( - key: String, - oldKey: String?, - data: MediaDeviceData? - ) { - if (oldKey != null && oldKey != key && entries.contains(oldKey)) { - entries[key] = entries.remove(oldKey)?.first to data - update(key, oldKey) - } else { - entries[key] = entries[key]?.first to data - update(key, key) - } - } - override fun onKeyRemoved(key: String) { - remove(key) - } - }) + override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = data to entries.remove(oldKey)?.second + update(key, oldKey) + } else { + entries[key] = data to entries[key]?.second + update(key, key) + } } - /** - * Get a map of all non-null data entries - */ - fun getData(): Map<String, MediaData> { - return entries.filter { - (key, pair) -> pair.first != null && pair.second != null - }.mapValues { - (key, pair) -> pair.first!!.copy(device = pair.second) + override fun onMediaDataRemoved(key: String) { + remove(key) + } + + override fun onMediaDeviceChanged( + key: String, + oldKey: String?, + data: MediaDeviceData? + ) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = entries.remove(oldKey)?.first to data + update(key, oldKey) + } else { + entries[key] = entries[key]?.first to data + update(key, key) } } + override fun onKeyRemoved(key: String) { + remove(key) + } + /** * Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index 662831e4a445..1f580a953d09 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -24,32 +24,32 @@ import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "MediaDataFilter" +private const val DEBUG = true /** * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user * switches (removing entries for the previous user, adding back entries for the current user) * - * This is added downstream of [MediaDataManager] since we may still need to handle callbacks from - * background users (e.g. timeouts) that UI classes should ignore. - * Instead, UI classes should listen to this so they can stay in sync with the current user. + * This is added at the end of the pipeline since we may still need to handle callbacks from + * background users (e.g. timeouts). */ -@Singleton class MediaDataFilter @Inject constructor( - private val dataSource: MediaDataCombineLatest, private val broadcastDispatcher: BroadcastDispatcher, private val mediaResumeListener: MediaResumeListener, - private val mediaDataManager: MediaDataManager, private val lockscreenUserManager: NotificationLockscreenUserManager, @Main private val executor: Executor ) : MediaDataManager.Listener { private val userTracker: CurrentUserTracker - private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + internal val listeners: Set<MediaDataManager.Listener> + get() = _listeners.toSet() + internal lateinit var mediaDataManager: MediaDataManager - // The filtered mediaEntries, which will be a subset of all mediaEntries in MediaDataManager - private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + private val allEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + // The filtered userEntries, which will be a subset of all userEntries in MediaDataManager + private val userEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() init { userTracker = object : CurrentUserTracker(broadcastDispatcher) { @@ -59,31 +59,34 @@ class MediaDataFilter @Inject constructor( } } userTracker.startTracking() - dataSource.addListener(this) } override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { + if (oldKey != null && oldKey != key) { + allEntries.remove(oldKey) + } + allEntries.put(key, data) + if (!lockscreenUserManager.isCurrentProfile(data.userId)) { return } - if (oldKey != null) { - mediaEntries.remove(oldKey) + if (oldKey != null && oldKey != key) { + userEntries.remove(oldKey) } - mediaEntries.put(key, data) + userEntries.put(key, data) // Notify listeners - val listenersCopy = listeners.toSet() - listenersCopy.forEach { + listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) } } override fun onMediaDataRemoved(key: String) { - mediaEntries.remove(key)?.let { + allEntries.remove(key) + userEntries.remove(key)?.let { // Only notify listeners if something actually changed - val listenersCopy = listeners.toSet() - listenersCopy.forEach { + listeners.forEach { it.onMediaDataRemoved(key) } } @@ -92,22 +95,22 @@ class MediaDataFilter @Inject constructor( @VisibleForTesting internal fun handleUserSwitched(id: Int) { // If the user changes, remove all current MediaData objects and inform listeners - val listenersCopy = listeners.toSet() - val keyCopy = mediaEntries.keys.toMutableList() + val listenersCopy = listeners + val keyCopy = userEntries.keys.toMutableList() // Clear the list first, to make sure callbacks from listeners if we have any entries // are up to date - mediaEntries.clear() + userEntries.clear() keyCopy.forEach { - Log.d(TAG, "Removing $it after user change") + if (DEBUG) Log.d(TAG, "Removing $it after user change") listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) } } - dataSource.getData().forEach { (key, data) -> + allEntries.forEach { (key, data) -> if (lockscreenUserManager.isCurrentProfile(data.userId)) { - Log.d(TAG, "Re-adding $key after user change") - mediaEntries.put(key, data) + if (DEBUG) Log.d(TAG, "Re-adding $key after user change") + userEntries.put(key, data) listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) } @@ -119,7 +122,8 @@ class MediaDataFilter @Inject constructor( * Invoked when the user has dismissed the media carousel */ fun onSwipeToDismiss() { - val mediaKeys = mediaEntries.keys.toSet() + if (DEBUG) Log.d(TAG, "Media carousel swiped away") + val mediaKeys = userEntries.keys.toSet() mediaKeys.forEach { mediaDataManager.setTimedOut(it, timedOut = true) } @@ -128,26 +132,20 @@ class MediaDataFilter @Inject constructor( /** * Are there any media notifications active? */ - fun hasActiveMedia() = mediaEntries.any { it.value.active } + fun hasActiveMedia() = userEntries.any { it.value.active } /** * Are there any media entries we should display? - * If resumption is enabled, this will include inactive players - * If resumption is disabled, we only want to show active players */ - fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) { - mediaEntries.isNotEmpty() - } else { - hasActiveMedia() - } + fun hasAnyMedia() = userEntries.isNotEmpty() /** * Add a listener for filtered [MediaData] changes */ - fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + fun addListener(listener: MediaDataManager.Listener) = _listeners.add(listener) /** * Remove a listener that was registered with addListener */ - fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) -}
\ No newline at end of file + fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener) +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 33475aca0bfb..bb11efeebf08 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -31,6 +31,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata +import android.media.session.MediaController import android.media.session.MediaSession import android.net.Uri import android.os.UserHandle @@ -44,6 +45,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert @@ -55,8 +57,6 @@ import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton -import kotlin.collections.ArrayList -import kotlin.collections.LinkedHashMap // URI fields to try loading album art from private val ART_URIS = arrayOf( @@ -66,6 +66,7 @@ private val ART_URIS = arrayOf( ) private const val TAG = "MediaDataManager" +private const val DEBUG = true private const val DEFAULT_LUMINOSITY = 0.25f private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f @@ -98,23 +99,23 @@ class MediaDataManager( dumpManager: DumpManager, mediaTimeoutListener: MediaTimeoutListener, mediaResumeListener: MediaResumeListener, + mediaSessionBasedFilter: MediaSessionBasedFilter, + mediaDeviceManager: MediaDeviceManager, + mediaDataCombineLatest: MediaDataCombineLatest, + private val mediaDataFilter: MediaDataFilter, private var useMediaResumption: Boolean, private val useQsMediaPlayer: Boolean ) : Dumpable { - private val listeners: MutableSet<Listener> = mutableSetOf() + // Internal listeners are part of the internal pipeline. External listeners (those registered + // with [MediaDeviceManager.addListener]) receive events after they have propagated through + // the internal pipeline. + // Another way to think of the distinction between internal and external listeners is the + // following. Internal listeners are listeners that MediaDataManager depends on, and external + // listeners are listeners that depend on MediaDataManager. + // TODO(b/159539991#comment5): Move internal listeners to separate package. + private val internalListeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() - internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context) - set(value) { - // Update list - appsBlockedFromResume.clear() - appsBlockedFromResume.addAll(value) - - // Remove any existing resume players that are now blocked - appsBlockedFromResume.forEach { - removeAllForPackage(it) - } - } @Inject constructor( @@ -125,9 +126,14 @@ class MediaDataManager( dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, mediaTimeoutListener: MediaTimeoutListener, - mediaResumeListener: MediaResumeListener + mediaResumeListener: MediaResumeListener, + mediaSessionBasedFilter: MediaSessionBasedFilter, + mediaDeviceManager: MediaDeviceManager, + mediaDataCombineLatest: MediaDataCombineLatest, + mediaDataFilter: MediaDataFilter ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory, broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener, + mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context)) private val appChangeReceiver = object : BroadcastReceiver() { @@ -150,12 +156,26 @@ class MediaDataManager( init { dumpManager.registerDumpable(TAG, this) + + // Initialize the internal processing pipeline. The listeners at the front of the pipeline + // are set as internal listeners so that they receive events. From there, events are + // propagated through the pipeline. The end of the pipeline is currently mediaDataFilter, + // so it is responsible for dispatching events to external listeners. To achieve this, + // external listeners that are registered with [MediaDataManager.addListener] are actually + // registered as listeners to mediaDataFilter. + addInternalListener(mediaTimeoutListener) + addInternalListener(mediaResumeListener) + addInternalListener(mediaSessionBasedFilter) + mediaSessionBasedFilter.addListener(mediaDeviceManager) + mediaSessionBasedFilter.addListener(mediaDataCombineLatest) + mediaDeviceManager.addListener(mediaDataCombineLatest) + mediaDataCombineLatest.addListener(mediaDataFilter) + + // Set up links back into the pipeline for listeners that need to send events upstream. mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean -> setTimedOut(token, timedOut) } - addListener(mediaTimeoutListener) - mediaResumeListener.setManager(this) - addListener(mediaResumeListener) + mediaDataFilter.mediaDataManager = this val suspendFilter = IntentFilter(Intent.ACTION_PACKAGES_SUSPENDED) broadcastDispatcher.registerReceiver(appChangeReceiver, suspendFilter, null, UserHandle.ALL) @@ -193,10 +213,9 @@ class MediaDataManager( private fun removeAllForPackage(packageName: String) { Assert.isMainThread() - val listenersCopy = listeners.toSet() val toRemove = mediaEntries.filter { it.value.packageName == packageName } toRemove.forEach { - removeEntry(it.key, listenersCopy) + removeEntry(it.key) } } @@ -256,15 +275,48 @@ class MediaDataManager( /** * Add a listener for changes in this class */ - fun addListener(listener: Listener) = listeners.add(listener) + fun addListener(listener: Listener) { + // mediaDataFilter is the current end of the internal pipeline. Register external + // listeners as listeners to it. + mediaDataFilter.addListener(listener) + } /** * Remove a listener for changes in this class */ - fun removeListener(listener: Listener) = listeners.remove(listener) + fun removeListener(listener: Listener) { + // Since mediaDataFilter is the current end of the internal pipelie, external listeners + // have been registered to it. So, they need to be removed from it too. + mediaDataFilter.removeListener(listener) + } + + /** + * Add a listener for internal events. + */ + private fun addInternalListener(listener: Listener) = internalListeners.add(listener) + + /** + * Notify internal listeners of loaded event. + * + * External listeners registered with [addListener] will be notified after the event propagates + * through the internal listener pipeline. + */ + private fun notifyMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + internalListeners.forEach { it.onMediaDataLoaded(key, oldKey, info) } + } + + /** + * Notify internal listeners of removed event. + * + * External listeners registered with [addListener] will be notified after the event propagates + * through the internal listener pipeline. + */ + private fun notifyMediaDataRemoved(key: String) { + internalListeners.forEach { it.onMediaDataRemoved(key) } + } /** - * Called whenever the player has been paused or stopped for a while. + * Called whenever the player has been paused or stopped for a while, or swiped from QQS. * This will make the player not active anymore, hiding it from QQS and Keyguard. * @see MediaData.active */ @@ -274,20 +326,28 @@ class MediaDataManager( return } it.active = !timedOut + if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut") onMediaDataLoaded(token, token, it) } } - private fun removeEntry(key: String, listenersCopy: Set<Listener>) { + private fun removeEntry(key: String) { mediaEntries.remove(key) - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } + notifyMediaDataRemoved(key) } fun dismissMediaData(key: String, delay: Long) { - val listenersCopy = listeners.toSet() - foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay) + backgroundExecutor.execute { + mediaEntries[key]?.let { mediaData -> + if (mediaData.isLocalSession) { + mediaData.token?.let { + val mediaController = mediaControllerFactory.create(it) + mediaController.transportControls.stop() + } + } + } + } + foregroundExecutor.executeDelayed({ removeEntry(key) }, delay) } private fun loadMediaDataInBgForResumption( @@ -306,7 +366,9 @@ class MediaDataManager( return } - Log.d(TAG, "adding track for $userId from browser: $desc") + if (DEBUG) { + Log.d(TAG, "adding track for $userId from browser: $desc") + } // Album art var artworkBitmap = desc.iconBitmap @@ -337,7 +399,8 @@ class MediaDataManager( ) { val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? - val metadata = mediaControllerFactory.create(token).metadata + val mediaController = mediaControllerFactory.create(token) + val metadata = mediaController.metadata // Foreground and Background colors computed from album art val notif: Notification = sbn.notification @@ -406,7 +469,7 @@ class MediaDataManager( if (actions != null) { for ((index, action) in actions.withIndex()) { if (action.getIcon() == null) { - Log.i(TAG, "No icon for action $index ${action.title}") + if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}") actionsToShowCollapsed.remove(index) continue } @@ -429,6 +492,10 @@ class MediaDataManager( } } + val isLocalSession = mediaController.playbackInfo?.playbackType == + MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true + val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null + foregroundExecutor.execute { val resumeAction: Runnable? = mediaEntries[key]?.resumeAction val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true @@ -436,8 +503,9 @@ class MediaDataManager( onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null, - active, resumeAction = resumeAction, notificationKey = key, - hasCheckedForResume = hasCheckedForResume)) + active, resumeAction = resumeAction, isLocalSession = isLocalSession, + notificationKey = key, hasCheckedForResume = hasCheckedForResume, + isPlaying = isPlaying, isClearable = sbn.isClearable())) } } @@ -450,7 +518,7 @@ class MediaDataManager( if (!TextUtils.isEmpty(uriString)) { val albumArt = loadBitmapFromUri(Uri.parse(uriString)) if (albumArt != null) { - Log.d(TAG, "loaded art from $uri") + if (DEBUG) Log.d(TAG, "loaded art from $uri") return albumArt } } @@ -481,7 +549,10 @@ class MediaDataManager( decoder, info, source -> decoder.isMutableRequired = true } } catch (e: IOException) { - e.printStackTrace() + Log.e(TAG, "Unable to load bitmap", e) + null + } catch (e: RuntimeException) { + Log.e(TAG, "Unable to load bitmap", e) null } } @@ -526,19 +597,15 @@ class MediaDataManager( if (mediaEntries.containsKey(key)) { // Otherwise this was removed already mediaEntries.put(key, data) - val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataLoaded(key, oldKey, data) - } + notifyMediaDataLoaded(key, oldKey, data) } } fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) - if (useMediaResumption && removed?.resumeAction != null && - !isBlockedFromResume(removed?.packageName)) { - Log.d(TAG, "Not removing $key because resumable") + if (useMediaResumption && removed?.resumeAction != null) { + if (DEBUG) Log.d(TAG, "Not removing $key because resumable") // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = getResumeMediaAction(removed.resumeAction!!) val updated = removed.copy(token = null, actions = listOf(resumeAction), @@ -546,39 +613,22 @@ class MediaDataManager( val pkg = removed?.packageName val migrate = mediaEntries.put(pkg, updated) == null // Notify listeners of "new" controls when migrating or removed and update when not - val listenersCopy = listeners.toSet() if (migrate) { - listenersCopy.forEach { - it.onMediaDataLoaded(pkg, key, updated) - } + notifyMediaDataLoaded(pkg, key, updated) } else { // Since packageName is used for the key of the resumption controls, it is // possible that another notification has already been reused for the resumption // controls of this package. In this case, rather than renaming this player as // packageName, just remove it and then send a update to the existing resumption // controls. - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } - listenersCopy.forEach { - it.onMediaDataLoaded(pkg, pkg, updated) - } + notifyMediaDataRemoved(key) + notifyMediaDataLoaded(pkg, pkg, updated) } return } if (removed != null) { - val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } - } - } - - private fun isBlockedFromResume(packageName: String?): Boolean { - if (packageName == null) { - return true + notifyMediaDataRemoved(key) } - return appsBlockedFromResume.contains(packageName) } fun setMediaResumptionEnabled(isEnabled: Boolean) { @@ -590,17 +640,31 @@ class MediaDataManager( if (!useMediaResumption) { // Remove any existing resume controls - val listenersCopy = listeners.toSet() val filtered = mediaEntries.filter { !it.value.active } filtered.forEach { mediaEntries.remove(it.key) - listenersCopy.forEach { listener -> - listener.onMediaDataRemoved(it.key) - } + notifyMediaDataRemoved(it.key) } } } + /** + * Invoked when the user has dismissed the media carousel + */ + fun onSwipeToDismiss() = mediaDataFilter.onSwipeToDismiss() + + /** + * Are there any media notifications active? + */ + fun hasActiveMedia() = mediaDataFilter.hasActiveMedia() + + /** + * Are there any media entries we should display? + * If resumption is enabled, this will include inactive players + * If resumption is disabled, we only want to show active players + */ + fun hasAnyMedia() = mediaDataFilter.hasAnyMedia() + interface Listener { /** @@ -620,10 +684,10 @@ class MediaDataManager( override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { pw.apply { - println("listeners: $listeners") + println("internalListeners: $internalListeners") + println("externalListeners: ${mediaDataFilter.listeners}") println("mediaEntries: $mediaEntries") println("useMediaResumption: $useMediaResumption") - println("appsBlockedFromResume: $appsBlockedFromResume") } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 143f8496e7aa..a993d00df01e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -16,37 +16,40 @@ package com.android.systemui.media -import android.content.Context import android.media.MediaRouter2Manager import android.media.session.MediaController +import androidx.annotation.AnyThread +import androidx.annotation.MainThread +import androidx.annotation.WorkerThread import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice -import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.Dumpable +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import java.io.FileDescriptor import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton + +private const val PLAYBACK_TYPE_UNKNOWN = 0 /** * Provides information about the route (ie. device) where playback is occurring. */ -@Singleton class MediaDeviceManager @Inject constructor( - private val context: Context, + private val controllerFactory: MediaControllerFactory, private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, @Main private val fgExecutor: Executor, - private val mediaDataManager: MediaDataManager, - private val dumpManager: DumpManager + @Background private val bgExecutor: Executor, + dumpManager: DumpManager ) : MediaDataManager.Listener, Dumpable { + private val listeners: MutableSet<Listener> = mutableSetOf() - private val entries: MutableMap<String, Token> = mutableMapOf() + private val entries: MutableMap<String, Entry> = mutableMapOf() init { - mediaDataManager.addListener(this) dumpManager.registerDumpable(javaClass.name, this) } @@ -69,9 +72,9 @@ class MediaDeviceManager @Inject constructor( if (entry == null || entry?.token != data.token) { entry?.stop() val controller = data.token?.let { - MediaController(context, it) + controllerFactory.create(it) } - entry = Token(key, oldKey, controller, + entry = Entry(key, oldKey, controller, localMediaManagerFactory.create(data.packageName)) entries[key] = entry entry.start() @@ -99,6 +102,7 @@ class MediaDeviceManager @Inject constructor( } } + @MainThread private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) { val enabled = device != null val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name) @@ -114,52 +118,82 @@ class MediaDeviceManager @Inject constructor( fun onKeyRemoved(key: String) } - private inner class Token( + private inner class Entry( val key: String, val oldKey: String?, val controller: MediaController?, val localMediaManager: LocalMediaManager - ) : LocalMediaManager.DeviceCallback { + ) : LocalMediaManager.DeviceCallback, MediaController.Callback() { + val token get() = controller?.sessionToken private var started = false + private var playbackType = PLAYBACK_TYPE_UNKNOWN private var current: MediaDevice? = null set(value) { if (!started || value != field) { field = value - processDevice(key, oldKey, value) + fgExecutor.execute { + processDevice(key, oldKey, value) + } } } - fun start() { + + @AnyThread + fun start() = bgExecutor.execute { localMediaManager.registerCallback(this) localMediaManager.startScan() + playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN + controller?.registerCallback(this) updateCurrent() started = true } - fun stop() { + + @AnyThread + fun stop() = bgExecutor.execute { started = false + controller?.unregisterCallback(this) localMediaManager.stopScan() localMediaManager.unregisterCallback(this) } + fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { - val route = controller?.let { + val routingSession = controller?.let { mr2manager.getRoutingSessionForMediaController(it) } + val selectedRoutes = routingSession?.let { + mr2manager.getSelectedRoutes(it) + } with(pw) { println(" current device is ${current?.name}") val type = controller?.playbackInfo?.playbackType - println(" PlaybackType=$type (1 for local, 2 for remote)") - println(" route=$route") + println(" PlaybackType=$type (1 for local, 2 for remote) cached=$playbackType") + println(" routingSession=$routingSession") + println(" selectedRoutes=$selectedRoutes") + } + } + + @WorkerThread + override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) { + val newPlaybackType = info?.playbackType ?: PLAYBACK_TYPE_UNKNOWN + if (newPlaybackType == playbackType) { + return } + playbackType = newPlaybackType + updateCurrent() } - override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute { + + override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute { updateCurrent() } + override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) { - fgExecutor.execute { + bgExecutor.execute { updateCurrent() } } + + @WorkerThread private fun updateCurrent() { val device = localMediaManager.getCurrentConnectedDevice() controller?.let { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 3598719fcb3a..ce184aa23a57 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -14,7 +14,7 @@ import javax.inject.Inject class MediaHost @Inject constructor( private val state: MediaHostStateHolder, private val mediaHierarchyManager: MediaHierarchyManager, - private val mediaDataFilter: MediaDataFilter, + private val mediaDataManager: MediaDataManager, private val mediaHostStatesManager: MediaHostStatesManager ) : MediaHostState by state { lateinit var hostView: UniqueObjectHostView @@ -79,12 +79,12 @@ class MediaHost @Inject constructor( // be a delay until the views and the controllers are initialized, leaving us // with either a blank view or the controllers not yet initialized and the // measuring wrong - mediaDataFilter.addListener(listener) + mediaDataManager.addListener(listener) updateViewVisibility() } override fun onViewDetachedFromWindow(v: View?) { - mediaDataFilter.removeListener(listener) + mediaDataManager.removeListener(listener) } }) @@ -113,9 +113,9 @@ class MediaHost @Inject constructor( private fun updateViewVisibility() { visible = if (showsOnlyActiveMedia) { - mediaDataFilter.hasActiveMedia() + mediaDataManager.hasActiveMedia() } else { - mediaDataFilter.hasAnyMedia() + mediaDataManager.hasAnyMedia() } val newVisibility = if (visible) View.VISIBLE else View.GONE if (newVisibility != hostView.visibility) { @@ -289,4 +289,4 @@ interface MediaHostState { * Get a copy of this view state, deepcopying all appropriate members */ fun copy(): MediaHostState -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index c41712c4cf10..936db8735ad8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -28,6 +28,7 @@ import android.os.UserHandle import android.provider.Settings import android.service.media.MediaBrowserService import android.util.Log +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.tuner.TunerService @@ -47,19 +48,20 @@ class MediaResumeListener @Inject constructor( private val context: Context, private val broadcastDispatcher: BroadcastDispatcher, @Background private val backgroundExecutor: Executor, - private val tunerService: TunerService + private val tunerService: TunerService, + private val mediaBrowserFactory: ResumeMediaBrowserFactory ) : MediaDataManager.Listener { private var useMediaResumption: Boolean = Utils.useMediaResumption(context) private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue() - private var blockedApps: MutableSet<String> = Utils.getBlockedMediaApps(context) private lateinit var mediaDataManager: MediaDataManager private var mediaBrowser: ResumeMediaBrowser? = null private var currentUserId: Int = context.userId - private val userChangeReceiver = object : BroadcastReceiver() { + @VisibleForTesting + val userChangeReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (Intent.ACTION_USER_UNLOCKED == intent.action) { loadMediaResumptionControls() @@ -115,18 +117,8 @@ class MediaResumeListener @Inject constructor( mediaDataManager.setMediaResumptionEnabled(useMediaResumption) } }, Settings.Secure.MEDIA_CONTROLS_RESUME) - - // Listen to changes in which apps are allowed to persist - tunerService.addTunable(object : TunerService.Tunable { - override fun onTuningChanged(key: String?, newValue: String?) { - blockedApps = Utils.getBlockedMediaApps(context) - mediaDataManager.appsBlockedFromResume = blockedApps - } - }, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED) } - fun isResumptionEnabled() = useMediaResumption - private fun loadSavedComponents() { // Make sure list is empty (if we switched users) resumeComponents.clear() @@ -153,10 +145,8 @@ class MediaResumeListener @Inject constructor( } resumeComponents.forEach { - if (!blockedApps.contains(it.packageName)) { - val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it) - browser.findRecentMedia() - } + val browser = mediaBrowserFactory.create(mediaBrowserCallback, it) + browser.findRecentMedia() } } @@ -165,8 +155,7 @@ class MediaResumeListener @Inject constructor( // If this had been started from a resume state, disconnect now that it's live mediaBrowser?.disconnect() // If we don't have a resume action, check if we haven't already - if (data.resumeAction == null && !data.hasCheckedForResume && - !blockedApps.contains(data.packageName)) { + if (data.resumeAction == null && !data.hasCheckedForResume) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager @@ -195,14 +184,10 @@ class MediaResumeListener @Inject constructor( private fun tryUpdateResumptionList(key: String, componentName: ComponentName) { Log.d(TAG, "Testing if we can connect to $componentName") mediaBrowser?.disconnect() - mediaBrowser = ResumeMediaBrowser(context, + mediaBrowser = mediaBrowserFactory.create( object : ResumeMediaBrowser.Callback() { override fun onConnected() { - Log.d(TAG, "yes we can resume with $componentName") - mediaDataManager.setResumeAction(key, getResumeAction(componentName)) - updateResumptionList(componentName) - mediaBrowser?.disconnect() - mediaBrowser = null + Log.d(TAG, "Connected to $componentName") } override fun onError() { @@ -211,6 +196,19 @@ class MediaResumeListener @Inject constructor( mediaBrowser?.disconnect() mediaBrowser = null } + + override fun addTrack( + desc: MediaDescription, + component: ComponentName, + browser: ResumeMediaBrowser + ) { + // Since this is a test, just save the component for later + Log.d(TAG, "Can get resumable media from $componentName") + mediaDataManager.setResumeAction(key, getResumeAction(componentName)) + updateResumptionList(componentName) + mediaBrowser?.disconnect() + mediaBrowser = null + } }, componentName) mediaBrowser?.testConnection() @@ -247,7 +245,7 @@ class MediaResumeListener @Inject constructor( private fun getResumeAction(componentName: ComponentName): Runnable { return Runnable { mediaBrowser?.disconnect() - mediaBrowser = ResumeMediaBrowser(context, + mediaBrowser = mediaBrowserFactory.create( object : ResumeMediaBrowser.Callback() { override fun onConnected() { if (mediaBrowser?.token == null) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt new file mode 100644 index 000000000000..f695622b943a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.content.ComponentName +import android.content.Context +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.util.Log +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins +import java.util.concurrent.Executor +import javax.inject.Inject + +private const val TAG = "MediaSessionBasedFilter" + +/** + * Filters media loaded events for local media sessions while an app is casting. + * + * When an app is casting there can be one remote media sessions and potentially more local media + * sessions. In this situation, there should only be a media object for the remote session. To + * achieve this, update events for the local session need to be filtered. + */ +class MediaSessionBasedFilter @Inject constructor( + context: Context, + private val sessionManager: MediaSessionManager, + @Main private val foregroundExecutor: Executor, + @Background private val backgroundExecutor: Executor +) : MediaDataManager.Listener { + + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + + // Keep track of MediaControllers for a given package to check if an app is casting and it + // filter loaded events for local sessions. + private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> = + LinkedHashMap() + + // Keep track of the key used for the session tokens. This information is used to know when to + // dispatch a removed event so that a media object for a local session will be removed. + private val keyedTokens: MutableMap<String, MutableSet<MediaSession.Token>> = mutableMapOf() + + // Keep track of which media session tokens have associated notifications. + private val tokensWithNotifications: MutableSet<MediaSession.Token> = mutableSetOf() + + private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener { + override fun onActiveSessionsChanged(controllers: List<MediaController>) { + handleControllersChanged(controllers) + } + } + + init { + backgroundExecutor.execute { + val name = ComponentName(context, NotificationListenerWithPlugins::class.java) + sessionManager.addOnActiveSessionsChangedListener(sessionListener, name) + handleControllersChanged(sessionManager.getActiveSessions(name)) + } + } + + /** + * Add a listener for filtered [MediaData] changes + */ + fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + + /** + * Remove a listener that was registered with addListener + */ + fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) + + /** + * May filter loaded events by not passing them along to listeners. + * + * If an app has only one session with playback type PLAYBACK_TYPE_REMOTE, then assuming that + * the app is casting. Sometimes apps will send redundant updates to a local session with + * playback type PLAYBACK_TYPE_LOCAL. These updates should be filtered to improve the usability + * of the media controls. + */ + override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + backgroundExecutor.execute { + info.token?.let { + tokensWithNotifications.add(it) + } + val isMigration = oldKey != null && key != oldKey + if (isMigration) { + keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) } + } + if (info.token != null) { + keyedTokens.get(key)?.let { + tokens -> + tokens.add(info.token) + } ?: run { + val tokens = mutableSetOf(info.token) + keyedTokens.put(key, tokens) + } + } + // Determine if an app is casting by checking if it has a session with playback type + // PLAYBACK_TYPE_REMOTE. + val remoteControllers = packageControllers.get(info.packageName)?.filter { + it.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE + } + // Limiting search to only apps with a single remote session. + val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null + if (isMigration || remote == null || remote.sessionToken == info.token || + !tokensWithNotifications.contains(remote.sessionToken)) { + // Not filtering in this case. Passing the event along to listeners. + dispatchMediaDataLoaded(key, oldKey, info) + } else { + // Filtering this event because the app is casting and the loaded events is for a + // local session. + Log.d(TAG, "filtering key=$key local=${info.token} remote=${remote?.sessionToken}") + // If the local session uses a different notification key, then lets go a step + // farther and dismiss the media data so that media controls for the local session + // don't hang around while casting. + if (!keyedTokens.get(key)!!.contains(remote.sessionToken)) { + dispatchMediaDataRemoved(key) + } + } + } + } + + override fun onMediaDataRemoved(key: String) { + // Queue on background thread to ensure ordering of loaded and removed events is maintained. + backgroundExecutor.execute { + keyedTokens.remove(key) + dispatchMediaDataRemoved(key) + } + } + + private fun dispatchMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + foregroundExecutor.execute { + listeners.toSet().forEach { it.onMediaDataLoaded(key, oldKey, info) } + } + } + + private fun dispatchMediaDataRemoved(key: String) { + foregroundExecutor.execute { + listeners.toSet().forEach { it.onMediaDataRemoved(key) } + } + } + + private fun handleControllersChanged(controllers: List<MediaController>) { + packageControllers.clear() + controllers.forEach { + controller -> + packageControllers.get(controller.packageName)?.let { + tokens -> + tokens.add(controller) + } ?: run { + val tokens = mutableListOf(controller) + packageControllers.put(controller.packageName, tokens) + } + } + tokensWithNotifications.retainAll(controllers.map { it.sessionToken }) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index 8662aacfdab2..dcb7767a680a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -54,32 +54,35 @@ class MediaTimeoutListener @Inject constructor( if (mediaListeners.containsKey(key)) { return } - // Having an old key means that we're migrating from/to resumption. We should invalidate - // the old listener and create a new one. + // Having an old key means that we're migrating from/to resumption. We should update + // the old listener to make sure that events will be dispatched to the new location. val migrating = oldKey != null && key != oldKey var wasPlaying = false if (migrating) { - if (mediaListeners.containsKey(oldKey)) { - val oldListener = mediaListeners.remove(oldKey) - wasPlaying = oldListener?.playing ?: false - oldListener?.destroy() + val reusedListener = mediaListeners.remove(oldKey) + if (reusedListener != null) { + wasPlaying = reusedListener.playing ?: false if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption") + reusedListener.mediaData = data + reusedListener.key = key + mediaListeners[key] = reusedListener + if (wasPlaying != reusedListener.playing) { + // If a player becomes active because of a migration, we'll need to broadcast + // its state. Doing it now would lead to reentrant callbacks, so let's wait + // until we're done. + mainExecutor.execute { + if (mediaListeners[key]?.playing == true) { + if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key") + timeoutCallback.invoke(key, false /* timedOut */) + } + } + } + return } else { Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...") } } mediaListeners[key] = PlaybackStateListener(key, data) - - // If a player becomes active because of a migration, we'll need to broadcast its state. - // Doing it now would lead to reentrant callbacks, so let's wait until we're done. - if (migrating && mediaListeners[key]?.playing != wasPlaying) { - mainExecutor.execute { - if (mediaListeners[key]?.playing == true) { - if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key") - timeoutCallback.invoke(key, false /* timedOut */) - } - } - } } override fun onMediaDataRemoved(key: String) { @@ -91,30 +94,39 @@ class MediaTimeoutListener @Inject constructor( } private inner class PlaybackStateListener( - private val key: String, + var key: String, data: MediaData ) : MediaController.Callback() { var timedOut = false var playing: Boolean? = null + var mediaData: MediaData = data + set(value) { + mediaController?.unregisterCallback(this) + field = value + mediaController = if (field.token != null) { + mediaControllerFactory.create(field.token) + } else { + null + } + mediaController?.registerCallback(this) + // Let's register the cancellations, but not dispatch events now. + // Timeouts didn't happen yet and reentrant events are troublesome. + processState(mediaController?.playbackState, dispatchEvents = false) + } + // Resume controls may have null token - private val mediaController = if (data.token != null) { - mediaControllerFactory.create(data.token) - } else { - null - } + private var mediaController: MediaController? = null private var cancellation: Runnable? = null init { - mediaController?.registerCallback(this) - // Let's register the cancellations, but not dispatch events now. - // Timeouts didn't happen yet and reentrant events are troublesome. - processState(mediaController?.playbackState, dispatchEvents = false) + mediaData = data } fun destroy() { mediaController?.unregisterCallback(this) + cancellation?.run() } override fun onPlaybackStateChanged(state: PlaybackState?) { @@ -171,4 +183,4 @@ class MediaTimeoutListener @Inject constructor( cancellation = null } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java index 68b6785849aa..a4d44367be73 100644 --- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java +++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java @@ -30,6 +30,8 @@ import android.service.media.MediaBrowserService; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.util.List; /** @@ -46,6 +48,7 @@ public class ResumeMediaBrowser { private static final String TAG = "ResumeMediaBrowser"; private final Context mContext; private final Callback mCallback; + private MediaBrowserFactory mBrowserFactory; private MediaBrowser mMediaBrowser; private ComponentName mComponentName; @@ -55,10 +58,12 @@ public class ResumeMediaBrowser { * @param callback used to report media items found * @param componentName Component name of the MediaBrowserService this browser will connect to */ - public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName) { + public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName, + MediaBrowserFactory browserFactory) { mContext = context; mCallback = callback; mComponentName = componentName; + mBrowserFactory = browserFactory; } /** @@ -74,7 +79,7 @@ public class ResumeMediaBrowser { disconnect(); Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); - mMediaBrowser = new MediaBrowser(mContext, + mMediaBrowser = mBrowserFactory.create( mComponentName, mConnectionCallback, rootHints); @@ -88,17 +93,19 @@ public class ResumeMediaBrowser { List<MediaBrowser.MediaItem> children) { if (children.size() == 0) { Log.d(TAG, "No children found for " + mComponentName); - return; - } - // We ask apps to return a playable item as the first child when sending - // a request with EXTRA_RECENT; if they don't, no resume controls - MediaBrowser.MediaItem child = children.get(0); - MediaDescription desc = child.getDescription(); - if (child.isPlayable() && mMediaBrowser != null) { - mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), - ResumeMediaBrowser.this); + mCallback.onError(); } else { - Log.d(TAG, "Child found but not playable for " + mComponentName); + // We ask apps to return a playable item as the first child when sending + // a request with EXTRA_RECENT; if they don't, no resume controls + MediaBrowser.MediaItem child = children.get(0); + MediaDescription desc = child.getDescription(); + if (child.isPlayable() && mMediaBrowser != null) { + mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), + ResumeMediaBrowser.this); + } else { + Log.d(TAG, "Child found but not playable for " + mComponentName); + mCallback.onError(); + } } disconnect(); } @@ -131,7 +138,7 @@ public class ResumeMediaBrowser { Log.d(TAG, "Service connected for " + mComponentName); if (mMediaBrowser != null && mMediaBrowser.isConnected()) { String root = mMediaBrowser.getRoot(); - if (!TextUtils.isEmpty(root)) { + if (!TextUtils.isEmpty(root) && mMediaBrowser != null) { mCallback.onConnected(); mMediaBrowser.subscribe(root, mSubscriptionCallback); return; @@ -182,7 +189,7 @@ public class ResumeMediaBrowser { disconnect(); Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); - mMediaBrowser = new MediaBrowser(mContext, mComponentName, + mMediaBrowser = mBrowserFactory.create(mComponentName, new MediaBrowser.ConnectionCallback() { @Override public void onConnected() { @@ -192,7 +199,7 @@ public class ResumeMediaBrowser { return; } MediaSession.Token token = mMediaBrowser.getSessionToken(); - MediaController controller = new MediaController(mContext, token); + MediaController controller = createMediaController(token); controller.getTransportControls(); controller.getTransportControls().prepare(); controller.getTransportControls().play(); @@ -212,6 +219,11 @@ public class ResumeMediaBrowser { mMediaBrowser.connect(); } + @VisibleForTesting + protected MediaController createMediaController(MediaSession.Token token) { + return new MediaController(mContext, token); + } + /** * Get the media session token * @return the token, or null if the MediaBrowser is null or disconnected @@ -235,42 +247,19 @@ public class ResumeMediaBrowser { /** * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser. - * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called - * depending on whether it was successful. + * If it can connect, ResumeMediaBrowser.Callback#onConnected will be called. If valid media is + * found, then ResumeMediaBrowser.Callback#addTrack will also be called. This allows for more + * detailed logging if the service has issues. If it cannot connect, or cannot find valid media, + * then ResumeMediaBrowser.Callback#onError will be called. * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed. */ public void testConnection() { disconnect(); - final MediaBrowser.ConnectionCallback connectionCallback = - new MediaBrowser.ConnectionCallback() { - @Override - public void onConnected() { - Log.d(TAG, "connected"); - if (mMediaBrowser == null || !mMediaBrowser.isConnected() - || TextUtils.isEmpty(mMediaBrowser.getRoot())) { - mCallback.onError(); - } else { - mCallback.onConnected(); - } - } - - @Override - public void onConnectionSuspended() { - Log.d(TAG, "suspended"); - mCallback.onError(); - } - - @Override - public void onConnectionFailed() { - Log.d(TAG, "failed"); - mCallback.onError(); - } - }; Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); - mMediaBrowser = new MediaBrowser(mContext, + mMediaBrowser = mBrowserFactory.create( mComponentName, - connectionCallback, + mConnectionCallback, rootHints); mMediaBrowser.connect(); } diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java new file mode 100644 index 000000000000..2261aa5ac265 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media; + +import android.content.ComponentName; +import android.content.Context; + +import javax.inject.Inject; + +/** + * Testable wrapper around {@link ResumeMediaBrowser} constructor + */ +public class ResumeMediaBrowserFactory { + private final Context mContext; + private final MediaBrowserFactory mBrowserFactory; + + @Inject + public ResumeMediaBrowserFactory(Context context, MediaBrowserFactory browserFactory) { + mContext = context; + mBrowserFactory = browserFactory; + } + + /** + * Creates a new ResumeMediaBrowser. + * + * @param callback will be called on connection or error, and addTrack when media item found + * @param componentName component to browse + * @return + */ + public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback, + ComponentName componentName) { + return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index 1ae54d60d3fa..d789501ffdef 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -28,10 +28,14 @@ import com.android.systemui.R */ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarViewModel.Progress> { - val seekBarDefaultMaxHeight = holder.seekBar.context.resources + val seekBarEnabledMaxHeight = holder.seekBar.context.resources .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height) val seekBarDisabledHeight = holder.seekBar.context.resources .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_height) + val seekBarEnabledVerticalPadding = holder.seekBar.context.resources + .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_vertical_padding) + val seekBarDisabledVerticalPadding = holder.seekBar.context.resources + .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding) /** Updates seek bar views when the data model changes. */ @UiThread @@ -39,6 +43,7 @@ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarVi if (!data.enabled) { if (holder.seekBar.maxHeight != seekBarDisabledHeight) { holder.seekBar.maxHeight = seekBarDisabledHeight + setVerticalPadding(seekBarDisabledVerticalPadding) } holder.seekBar.setEnabled(false) holder.seekBar.getThumb().setAlpha(0) @@ -51,8 +56,9 @@ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarVi holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) holder.seekBar.setEnabled(data.seekAvailable) - if (holder.seekBar.maxHeight != seekBarDefaultMaxHeight) { - holder.seekBar.maxHeight = seekBarDefaultMaxHeight + if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) { + holder.seekBar.maxHeight = seekBarEnabledMaxHeight + setVerticalPadding(seekBarEnabledVerticalPadding) } data.duration?.let { @@ -67,4 +73,11 @@ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarVi it / DateUtils.SECOND_IN_MILLIS)) } } + + @UiThread + fun setVerticalPadding(padding: Int) { + val leftPadding = holder.seekBar.paddingLeft + val rightPadding = holder.seekBar.paddingRight + holder.seekBar.setPadding(leftPadding, padding, rightPadding, padding) + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 8d4ca5e46fae..54df53dbe6d7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -94,6 +94,36 @@ public class PipTaskOrganizer extends TaskOrganizer implements private static final int MSG_FINISH_RESIZE = 4; private static final int MSG_RESIZE_USER = 5; + // Not a complete set of states but serves what we want right now. + private enum State { + UNDEFINED(0), + TASK_APPEARED(1), + ENTERING_PIP(2), + EXITING_PIP(3); + + private final int mStateValue; + + State(int value) { + mStateValue = value; + } + + private boolean isInPip() { + return mStateValue >= TASK_APPEARED.mStateValue + && mStateValue != EXITING_PIP.mStateValue; + } + + /** + * Resize request can be initiated in other component, ignore if we are no longer in PIP, + * still waiting for animation or we're exiting from it. + * + * @return {@code true} if the resize request should be blocked/ignored. + */ + private boolean shouldBlockResizeRequest() { + return mStateValue < ENTERING_PIP.mStateValue + || mStateValue == EXITING_PIP.mStateValue; + } + } + private final Handler mMainHandler; private final Handler mUpdateHandler; private final PipBoundsHandler mPipBoundsHandler; @@ -188,8 +218,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements private ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mToken; private SurfaceControl mLeash; - private boolean mInPip; - private boolean mExitingPip; + private State mState = State.UNDEFINED; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @@ -241,11 +270,11 @@ public class PipTaskOrganizer extends TaskOrganizer implements } public boolean isInPip() { - return mInPip; + return mState.isInPip(); } public boolean isDeferringEnterPipAnimation() { - return mInPip && mShouldDeferEnteringPip; + return mState.isInPip() && mShouldDeferEnteringPip; } /** @@ -274,15 +303,20 @@ public class PipTaskOrganizer extends TaskOrganizer implements * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void exitPip(int animationDurationMs) { - if (!mInPip || mExitingPip || mToken == null) { + if (!mState.isInPip() || mToken == null) { Log.wtf(TAG, "Not allowed to exitPip in current state" - + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken); + + " mState=" + mState + " mToken=" + mToken); + return; + } + + final PipWindowConfigurationCompact config = mCompactState.remove(mToken.asBinder()); + if (config == null) { + Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken); return; } mPipUiEventLoggerLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); - final PipWindowConfigurationCompact config = mCompactState.remove(mToken.asBinder()); config.syncWithScreenOrientation(mRequestedOrientation, mPipBoundsHandler.getDisplayRotation()); final boolean orientationDiffers = config.getRotation() @@ -293,6 +327,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; if (orientationDiffers) { + mState = State.EXITING_PIP; // Send started callback though animation is ignored. sendOnPipTransitionStarted(direction); // Don't bother doing an animation if the display rotation differs or if it's in @@ -301,7 +336,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements WindowOrganizer.applyTransaction(wct); // Send finished callback though animation is ignored. sendOnPipTransitionFinished(direction); - mInPip = false; } else { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); @@ -320,11 +354,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements scheduleAnimateResizePip(mLastReportedBounds, destinationBounds, null /* sourceHintRect */, direction, animationDurationMs, null /* updateBoundsCallback */); - mInPip = false; + mState = State.EXITING_PIP; } }); } - mExitingPip = true; } private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { @@ -341,9 +374,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements * Removes PiP immediately. */ public void removePip() { - if (!mInPip || mExitingPip || mToken == null) { + if (!mState.isInPip() || mToken == null) { Log.wtf(TAG, "Not allowed to removePip in current state" - + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken); + + " mState=" + mState + " mToken=" + mToken); return; } @@ -355,7 +388,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements .setDuration(mEnterExitAnimationDuration) .start()); mCompactState.remove(mToken.asBinder()); - mExitingPip = true; + mState = State.EXITING_PIP; } private void removePipImmediately() { @@ -377,8 +410,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements Objects.requireNonNull(info, "Requires RunningTaskInfo"); mTaskInfo = info; mToken = mTaskInfo.token; - mInPip = true; - mExitingPip = false; + mState = State.TASK_APPEARED; mLeash = leash; mCompactState.put(mToken.asBinder(), new PipWindowConfigurationCompact(mTaskInfo.configuration.windowConfiguration)); @@ -410,6 +442,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); + mState = State.ENTERING_PIP; } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterExitAnimationDuration); mOneShotAnimationType = ANIM_TYPE_BOUNDS; @@ -455,6 +488,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .start()); + // mState is set right after the animation is kicked off to block any resize + // requests such as offsetPip that may have been called prior to the transition. + mState = State.ENTERING_PIP; } }); } @@ -507,7 +543,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { - if (!mInPip) { + if (!mState.isInPip()) { return; } final WindowContainerToken token = info.token; @@ -518,8 +554,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements } mShouldDeferEnteringPip = false; mPictureInPictureParams = null; - mInPip = false; - mExitingPip = false; + mState = State.UNDEFINED; mPipUiEventLoggerLogger.setTaskInfo(null); } @@ -554,7 +589,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements @Override public void onFixedRotationFinished(int displayId) { - if (mShouldDeferEnteringPip && mInPip) { + if (mShouldDeferEnteringPip && mState.isInPip()) { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); @@ -575,7 +610,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { - if (mInPip && fromRotation) { + if (mState.isInPip() && fromRotation) { // If we are rotating while there is a current animation, immediately cancel the // animation (remove the listeners so we don't trigger the normal finish resize // call that should only happen on the update thread) @@ -661,10 +696,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, int durationMs, Consumer<Rect> updateBoundsCallback) { - if (!mInPip) { + if (!mState.isInPip()) { // TODO: tend to use shouldBlockResizeRequest here as well but need to consider // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window - // container transaction callback and we want to set the mExitingPip immediately. + // container transaction callback and we want to set the mState immediately. return; } @@ -721,7 +756,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements private void scheduleFinishResizePip(Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, Consumer<Rect> updateBoundsCallback) { - if (shouldBlockResizeRequest()) { + if (mState.shouldBlockResizeRequest()) { return; } @@ -740,7 +775,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .resetScale(tx, mLeash, destinationBounds) - .round(tx, mLeash, mInPip); + .round(tx, mLeash, mState.isInPip()); return tx; } @@ -749,7 +784,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements */ public void scheduleOffsetPip(Rect originalBounds, int offset, int duration, Consumer<Rect> updateBoundsCallback) { - if (shouldBlockResizeRequest()) { + if (mState.shouldBlockResizeRequest()) { return; } if (mShouldDeferEnteringPip) { @@ -794,7 +829,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) - .round(tx, mLeash, mInPip); + .round(tx, mLeash, mState.isInPip()); tx.apply(); } @@ -926,16 +961,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements } /** - * Resize request can be initiated in other component, ignore if we are no longer in PIP - * or we're exiting from it. - * - * @return {@code true} if the resize request should be blocked/ignored. - */ - private boolean shouldBlockResizeRequest() { - return !mInPip || mExitingPip; - } - - /** * Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen. * * @param destinationBoundsOut contain the updated destination bounds if applicable @@ -963,7 +988,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements pw.println(innerPrefix + "mToken=" + mToken + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); - pw.println(innerPrefix + "mInPip=" + mInPip); + pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java index 5e2cd9c111b2..970203578e73 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -17,6 +17,7 @@ package com.android.systemui.pip; import android.app.TaskInfo; +import android.content.pm.PackageManager; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -31,27 +32,49 @@ import javax.inject.Singleton; @Singleton public class PipUiEventLogger { + private static final int INVALID_PACKAGE_UID = -1; + private final UiEventLogger mUiEventLogger; + private final PackageManager mPackageManager; - private TaskInfo mTaskInfo; + private String mPackageName; + private int mPackageUid = INVALID_PACKAGE_UID; @Inject - public PipUiEventLogger(UiEventLogger uiEventLogger) { + public PipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) { mUiEventLogger = uiEventLogger; + mPackageManager = packageManager; } public void setTaskInfo(TaskInfo taskInfo) { - mTaskInfo = taskInfo; + if (taskInfo == null) { + mPackageName = null; + mPackageUid = INVALID_PACKAGE_UID; + } else { + mPackageName = taskInfo.topActivity.getPackageName(); + mPackageUid = getUid(mPackageName, taskInfo.userId); + } } /** * Sends log via UiEvent, reference go/uievent for how to debug locally */ public void log(PipUiEventEnum event) { - if (mTaskInfo == null) { + if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) { return; } - mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName()); + mUiEventLogger.log(event, mPackageUid, mPackageName); + } + + private int getUid(String packageName, int userId) { + int uid = INVALID_PACKAGE_UID; + try { + uid = mPackageManager.getApplicationInfoAsUser( + packageName, 0 /* ApplicationInfoFlags */, userId).uid; + } catch (PackageManager.NameNotFoundException e) { + // do nothing. + } + return uid; } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 2a83aa06a237..586399c6dfd5 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -562,8 +562,10 @@ public class PipMenuActivity extends Activity { // TODO: Check if the action drawable has changed before we reload it action.getIcon().loadDrawableAsync(this, d -> { - d.setTint(Color.WHITE); - actionView.setImageDrawable(d); + if (d != null) { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + } }, mHandler); actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index ef73aa7cbbfe..badd8835cdd4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -80,6 +80,7 @@ public class PipResizeGestureHandler { private final Context mContext; private final PipBoundsHandler mPipBoundsHandler; private final PipMotionHelper mMotionHelper; + private final PipMenuActivityController mMenuController; private final int mDisplayId; private final Executor mMainExecutor; private final SysUiState mSysUiState; @@ -117,13 +118,14 @@ public class PipResizeGestureHandler { public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, - PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, - Runnable updateMovementBoundsRunnable, SysUiState sysUiState, - PipUiEventLogger pipUiEventLogger) { + PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController pipMenuController, + Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, + SysUiState sysUiState, PipUiEventLogger pipUiEventLogger) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); mPipBoundsHandler = pipBoundsHandler; + mMenuController = pipMenuController; mMotionHelper = motionHelper; mPipTaskOrganizer = pipTaskOrganizer; mMovementBoundsSupplier = movementBoundsSupplier; @@ -322,6 +324,10 @@ public class PipResizeGestureHandler { mInputMonitor.pilferPointers(); } if (mThresholdCrossed) { + if (mMenuController.isMenuActivityVisible()) { + mMenuController.hideMenuWithoutResize(); + mMenuController.hideMenu(); + } final Rect currentPipBounds = mMotionHelper.getBounds(); mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 0127ff310b78..11e609b2ffef 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -232,7 +232,7 @@ public class PipTouchHandler { mSnapAlgorithm, floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, - deviceConfig, pipTaskOrganizer, this::getMovementBounds, + deviceConfig, pipTaskOrganizer, menuController, this::getMovementBounds, this::updateMovementBounds, sysUiState, pipUiEventLogger); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 7cd1c7861164..44803aedc4ea 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -177,6 +177,16 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } @Override + public void endFakeDrag() { + try { + super.endFakeDrag(); + } catch (NullPointerException e) { + // Not sure what's going on. Let's log it + Log.e(TAG, "endFakeDrag called without velocityTracker", e); + } + } + + @Override public void computeScroll() { if (!mScroller.isFinished() && mScroller.computeScrollOffset()) { if (!isFakeDragging()) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index e738cec4962a..bffeb3ec3c70 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -14,11 +14,8 @@ package com.android.systemui.qs.customize; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; @@ -28,10 +25,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup; import androidx.recyclerview.widget.ItemTouchHelper; @@ -49,7 +47,6 @@ import com.android.systemui.qs.customize.TileQueryHelper.TileInfo; import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSIconViewImpl; -import com.android.systemui.statusbar.phone.SystemUIDialog; import java.util.ArrayList; import java.util.List; @@ -78,10 +75,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private final List<TileInfo> mTiles = new ArrayList<>(); private final ItemTouchHelper mItemTouchHelper; private final ItemDecoration mDecoration; - private final AccessibilityManager mAccessibilityManager; private final int mMinNumTiles; private int mEditIndex; private int mTileDividerIndex; + private int mFocusIndex; private boolean mNeedsFocus; private List<String> mCurrentSpecs; private List<TileInfo> mOtherTiles; @@ -90,17 +87,28 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private Holder mCurrentDrag; private int mAccessibilityAction = ACTION_NONE; private int mAccessibilityFromIndex; - private CharSequence mAccessibilityFromLabel; private QSTileHost mHost; private final UiEventLogger mUiEventLogger; + private final AccessibilityDelegateCompat mAccessibilityDelegate; + private RecyclerView mRecyclerView; public TileAdapter(Context context, UiEventLogger uiEventLogger) { mContext = context; mUiEventLogger = uiEventLogger; - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mItemTouchHelper = new ItemTouchHelper(mCallbacks); mDecoration = new TileItemDecoration(context); mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); + mAccessibilityDelegate = new TileAdapterDelegate(); + } + + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = null; } public void setHost(QSTileHost host) { @@ -130,7 +138,6 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta // Remove blank tile from last spot mTiles.remove(--mEditIndex); // Update the tile divider position - mTileDividerIndex--; notifyDataSetChanged(); } mAccessibilityAction = ACTION_NONE; @@ -241,14 +248,12 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } private void setSelectableForHeaders(View view) { - if (mAccessibilityManager.isTouchExplorationEnabled()) { - final boolean selectable = mAccessibilityAction == ACTION_NONE; - view.setFocusable(selectable); - view.setImportantForAccessibility(selectable - ? View.IMPORTANT_FOR_ACCESSIBILITY_YES - : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - view.setFocusableInTouchMode(selectable); - } + final boolean selectable = mAccessibilityAction == ACTION_NONE; + view.setFocusable(selectable); + view.setImportantForAccessibility(selectable + ? View.IMPORTANT_FOR_ACCESSIBILITY_YES + : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + view.setFocusableInTouchMode(selectable); } @Override @@ -285,12 +290,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta holder.mTileView.setVisibility(View.VISIBLE); holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); holder.mTileView.setContentDescription(mContext.getString( - R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, - position)); + R.string.accessibility_qs_edit_tile_add_to_position, position)); holder.mTileView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - selectPosition(holder.getAdapterPosition(), v); + selectPosition(holder.getLayoutPosition()); } }); focusOnHolder(holder); @@ -299,54 +303,49 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta TileInfo info = mTiles.get(position); - if (position > mEditIndex) { - info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_add_tile_label, info.state.label); - } else if (mAccessibilityAction == ACTION_ADD) { + final boolean selectable = 0 < position && position < mEditIndex; + if (selectable && mAccessibilityAction == ACTION_ADD) { info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position); - } else if (mAccessibilityAction == ACTION_MOVE) { + R.string.accessibility_qs_edit_tile_add_to_position, position); + } else if (selectable && mAccessibilityAction == ACTION_MOVE) { info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position); + R.string.accessibility_qs_edit_tile_move_to_position, position); } else { - info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_tile_label, position, info.state.label); + info.state.contentDescription = info.state.label; } + info.state.expandedAccessibilityClassName = ""; + holder.mTileView.handleStateChanged(info.state); holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem); + holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + holder.mTileView.setClickable(true); + holder.mTileView.setOnClickListener(null); + holder.mTileView.setFocusable(true); + holder.mTileView.setFocusableInTouchMode(true); - if (mAccessibilityManager.isTouchExplorationEnabled()) { - final boolean selectable = mAccessibilityAction == ACTION_NONE || position < mEditIndex; + if (mAccessibilityAction != ACTION_NONE) { holder.mTileView.setClickable(selectable); holder.mTileView.setFocusable(selectable); + holder.mTileView.setFocusableInTouchMode(selectable); holder.mTileView.setImportantForAccessibility(selectable ? View.IMPORTANT_FOR_ACCESSIBILITY_YES : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - holder.mTileView.setFocusableInTouchMode(selectable); if (selectable) { holder.mTileView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - int position = holder.getAdapterPosition(); + int position = holder.getLayoutPosition(); if (position == RecyclerView.NO_POSITION) return; if (mAccessibilityAction != ACTION_NONE) { - selectPosition(position, v); - } else { - if (position < mEditIndex && canRemoveTiles()) { - showAccessibilityDialog(position, v); - } else if (position < mEditIndex && !canRemoveTiles()) { - startAccessibleMove(position); - } else { - startAccessibleAdd(position); - } + selectPosition(position); } } }); - if (position == mAccessibilityFromIndex) { - focusOnHolder(holder); - } } } + if (position == mFocusIndex) { + focusOnHolder(holder); + } } private void focusOnHolder(Holder holder) { @@ -360,9 +359,13 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta int oldLeft, int oldTop, int oldRight, int oldBottom) { holder.mTileView.removeOnLayoutChangeListener(this); holder.mTileView.requestFocus(); + if (mAccessibilityAction == ACTION_NONE) { + holder.mTileView.clearFocus(); + } } }); mNeedsFocus = false; + mFocusIndex = RecyclerView.NO_POSITION; } } @@ -370,72 +373,77 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return mCurrentSpecs.size() > mMinNumTiles; } - private void selectPosition(int position, View v) { + private void selectPosition(int position) { if (mAccessibilityAction == ACTION_ADD) { // Remove the placeholder. mTiles.remove(mEditIndex--); - notifyItemRemoved(mEditIndex); } mAccessibilityAction = ACTION_NONE; - move(mAccessibilityFromIndex, position, v); + move(mAccessibilityFromIndex, position, false); + mFocusIndex = position; + mNeedsFocus = true; notifyDataSetChanged(); } - private void showAccessibilityDialog(final int position, final View v) { - final TileInfo info = mTiles.get(position); - CharSequence[] options = new CharSequence[] { - mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label), - mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label), - }; - AlertDialog dialog = new Builder(mContext) - .setItems(options, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == 0) { - startAccessibleMove(position); - } else { - move(position, info.isSystem ? mEditIndex : mTileDividerIndex, v); - notifyItemChanged(mTileDividerIndex); - notifyDataSetChanged(); - } - } - }).setNegativeButton(android.R.string.cancel, null) - .create(); - SystemUIDialog.setShowForAllUsers(dialog, true); - SystemUIDialog.applyFlags(dialog); - dialog.show(); - } - private void startAccessibleAdd(int position) { mAccessibilityFromIndex = position; - mAccessibilityFromLabel = mTiles.get(position).state.label; mAccessibilityAction = ACTION_ADD; // Add placeholder for last slot. mTiles.add(mEditIndex++, null); // Update the tile divider position mTileDividerIndex++; + mFocusIndex = mEditIndex - 1; mNeedsFocus = true; + if (mRecyclerView != null) { + mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex)); + } notifyDataSetChanged(); } private void startAccessibleMove(int position) { mAccessibilityFromIndex = position; - mAccessibilityFromLabel = mTiles.get(position).state.label; mAccessibilityAction = ACTION_MOVE; + mFocusIndex = position; mNeedsFocus = true; notifyDataSetChanged(); } + private boolean canRemoveFromPosition(int position) { + return canRemoveTiles() && isCurrentTile(position); + } + + private boolean isCurrentTile(int position) { + return position < mEditIndex; + } + + private boolean canAddFromPosition(int position) { + return position > mEditIndex; + } + + private void addFromPosition(int position) { + if (!canAddFromPosition(position)) return; + move(position, mEditIndex); + } + + private void removeFromPosition(int position) { + if (!canRemoveFromPosition(position)) return; + TileInfo info = mTiles.get(position); + move(position, info.isSystem ? mEditIndex : mTileDividerIndex); + } + public SpanSizeLookup getSizeLookup() { return mSizeLookup; } - private boolean move(int from, int to, View v) { + private boolean move(int from, int to) { + return move(from, to, true); + } + + private boolean move(int from, int to, boolean notify) { if (to == from) { return true; } - CharSequence fromLabel = mTiles.get(from).state.label; - move(from, to, mTiles); + move(from, to, mTiles, notify); updateDividerLocations(); if (to >= mEditIndex) { mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to))); @@ -477,9 +485,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return spec; } - private <T> void move(int from, int to, List<T> list) { + private <T> void move(int from, int to, List<T> list, boolean notify) { list.add(to, list.remove(from)); - notifyItemMoved(from, to); + if (notify) { + notifyItemMoved(from, to); + } } public class Holder extends ViewHolder { @@ -491,6 +501,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0); mTileView.setBackground(null); mTileView.getIcon().disableAnimation(); + mTileView.setTag(this); + ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate); } } @@ -527,6 +539,46 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta .setDuration(DRAG_LENGTH) .alpha(.6f); } + + boolean canRemove() { + return canRemoveFromPosition(getLayoutPosition()); + } + + boolean canAdd() { + return canAddFromPosition(getLayoutPosition()); + } + + void toggleState() { + if (canAdd()) { + add(); + } else { + remove(); + } + } + + private void add() { + addFromPosition(getLayoutPosition()); + } + + private void remove() { + removeFromPosition(getLayoutPosition()); + } + + boolean isCurrentTile() { + return TileAdapter.this.isCurrentTile(getLayoutPosition()); + } + + void startAccessibleAdd() { + TileAdapter.this.startAccessibleAdd(getLayoutPosition()); + } + + void startAccessibleMove() { + TileAdapter.this.startAccessibleMove(getLayoutPosition()); + } + + boolean canTakeAccessibleAction() { + return mAccessibilityAction == ACTION_NONE; + } } private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() { @@ -648,7 +700,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta to == 0 || to == RecyclerView.NO_POSITION) { return false; } - return move(from, to, target.itemView); + return move(from, to); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java new file mode 100644 index 000000000000..1e426adac0b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.customize; + +import android.os.Bundle; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import androidx.core.view.AccessibilityDelegateCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; + +import com.android.systemui.R; + +import java.util.List; + +/** + * Accessibility delegate for {@link TileAdapter} views. + * + * This delegate will populate the accessibility info with the proper actions that can be taken for + * the different tiles: + * <ul> + * <li>Add to end if the tile is not a current tile (by double tap).</li> + * <li>Add to a given position (by context menu). This will let the user select a position.</li> + * <li>Remove, if the tile is a current tile (by double tap).</li> + * <li>Move to a given position (by context menu). This will let the user select a position.</li> + * </ul> + * + * This only handles generating the associated actions. The logic for selecting positions is handled + * by {@link TileAdapter}. + * + * In order for the delegate to work properly, the asociated {@link TileAdapter.Holder} should be + * passed along with the view using {@link View#setTag}. + */ +class TileAdapterDelegate extends AccessibilityDelegateCompat { + + private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position; + private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position; + + private TileAdapter.Holder getHolder(View view) { + return (TileAdapter.Holder) view.getTag(); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + TileAdapter.Holder holder = getHolder(host); + info.setCollectionItemInfo(null); + info.setStateDescription(""); + if (holder == null || !holder.canTakeAccessibleAction()) { + // If there's not a holder (not a regular Tile) or an action cannot be taken + // because we are in the middle of an accessibility action, don't create a special node. + return; + } + + addClickAction(host, info, holder); + maybeAddActionAddToPosition(host, info, holder); + maybeAddActionMoveToPosition(host, info, holder); + + if (holder.isCurrentTile()) { + info.setStateDescription(host.getContext().getString( + R.string.accessibility_qs_edit_position, holder.getLayoutPosition())); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + TileAdapter.Holder holder = getHolder(host); + + if (holder == null || !holder.canTakeAccessibleAction()) { + // If there's not a holder (not a regular Tile) or an action cannot be taken + // because we are in the middle of an accessibility action, perform the default action. + return super.performAccessibilityAction(host, action, args); + } + if (action == AccessibilityNodeInfo.ACTION_CLICK) { + holder.toggleState(); + return true; + } else if (action == MOVE_TO_POSITION_ID) { + holder.startAccessibleMove(); + return true; + } else if (action == ADD_TO_POSITION_ID) { + holder.startAccessibleAdd(); + return true; + } else { + return super.performAccessibilityAction(host, action, args); + } + } + + private void addClickAction( + View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) { + String clickActionString; + if (holder.canAdd()) { + clickActionString = host.getContext().getString( + R.string.accessibility_qs_edit_tile_add_action); + } else if (holder.canRemove()) { + clickActionString = host.getContext().getString( + R.string.accessibility_qs_edit_remove_tile_action); + } else { + // Remove the default click action if tile can't either be added or removed (for example + // if there's the minimum number of tiles) + List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> listOfActions = + info.getActionList(); // This is a copy + int numActions = listOfActions.size(); + for (int i = 0; i < numActions; i++) { + if (listOfActions.get(i).getId() == AccessibilityNodeInfo.ACTION_CLICK) { + info.removeAction(listOfActions.get(i)); + } + } + return; + } + + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat( + AccessibilityNodeInfo.ACTION_CLICK, clickActionString); + info.addAction(action); + } + + private void maybeAddActionMoveToPosition( + View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) { + if (holder.isCurrentTile()) { + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat(MOVE_TO_POSITION_ID, + host.getContext().getString( + R.string.accessibility_qs_edit_tile_start_move)); + info.addAction(action); + } + } + + private void maybeAddActionAddToPosition( + View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) { + if (holder.canAdd()) { + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat(ADD_TO_POSITION_ID, + host.getContext().getString( + R.string.accessibility_qs_edit_tile_start_add)); + info.addAction(action); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index bc03ca617dea..59597ac29e09 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -117,7 +117,8 @@ public class CellularTile extends QSTileImpl<SignalState> { return; } String carrierName = mController.getMobileDataNetworkName(); - if (TextUtils.isEmpty(carrierName)) { + boolean isInService = mController.isMobileDataNetworkInService(); + if (TextUtils.isEmpty(carrierName) || !isInService) { carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier); } AlertDialog dialog = new Builder(mContext) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 2f582727c766..acd2846fef3c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -60,7 +60,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements private static final String PATTERN_HOUR_MINUTE = "h:mm a"; private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm"; - private final ColorDisplayManager mManager; + private ColorDisplayManager mManager; private final LocationController mLocationController; private NightDisplayListener mListener; private boolean mIsListening; @@ -105,6 +105,8 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements mListener.setCallback(null); } + mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class); + // Make a new controller for the new user. mListener = new NightDisplayListener(mContext, newUserId, new Handler(Looper.myLooper())); if (mIsListening) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java index 7b83c20d4b86..f777553bb5fe 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java @@ -50,16 +50,15 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a"); private final Icon mIcon = ResourceIcon.get( com.android.internal.R.drawable.ic_qs_ui_mode_night); - private final UiModeManager mUiModeManager; + private UiModeManager mUiModeManager; private final BatteryController mBatteryController; private final LocationController mLocationController; - @Inject public UiModeNightTile(QSHost host, ConfigurationController configurationController, BatteryController batteryController, LocationController locationController) { super(host); mBatteryController = batteryController; - mUiModeManager = mContext.getSystemService(UiModeManager.class); + mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class); mLocationController = locationController; configurationController.observe(getLifecycle(), this); batteryController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index acc7f81eb722..8ec3db59117d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -41,6 +41,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import java.io.IOException; import java.util.concurrent.Executor; @@ -70,7 +71,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE"; private final RecordingController mController; - + private final KeyguardDismissUtil mKeyguardDismissUtil; private ScreenRecordingAudioSource mAudioSource; private boolean mShowTaps; private boolean mOriginalShowTaps; @@ -83,12 +84,13 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, UiEventLogger uiEventLogger, NotificationManager notificationManager, - CurrentUserContextTracker userContextTracker) { + CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { mController = controller; mLongExecutor = executor; mUiEventLogger = uiEventLogger; mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; + mKeyguardDismissUtil = keyguardDismissUtil; } /** @@ -168,16 +170,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis Intent shareIntent = new Intent(Intent.ACTION_SEND) .setType("video/mp4") .putExtra(Intent.EXTRA_STREAM, shareUri); - String shareLabel = getResources().getString(R.string.screenrecord_share_label); + mKeyguardDismissUtil.executeWhenUnlocked(() -> { + String shareLabel = getResources().getString(R.string.screenrecord_share_label); + startActivity(Intent.createChooser(shareIntent, shareLabel) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + // Remove notification + mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); + return false; + }, false); // Close quick shade sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - - // Remove notification - mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); - - startActivity(Intent.createChooser(shareIntent, shareLabel) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); break; } return Service.START_STICKY; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 853897fc54ae..c3c947bc40a8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -566,7 +566,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, boolean showFlash) { if (mScreenshotLayout.isAttachedToWindow()) { - if (!mDismissAnimation.isRunning()) { // if we didn't already dismiss for another reason + // if we didn't already dismiss for another reason + if (mDismissAnimation == null || !mDismissAnimation.isRunning()) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED); } dismissScreenshot("new screenshot requested", true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 739d30c2a707..8cd82cc77530 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_W import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; @@ -57,6 +58,7 @@ import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; @@ -234,8 +236,17 @@ public class NotificationMediaManager implements Dumpable { NotificationVisibility visibility, boolean removedByUser, int reason) { - onNotificationRemoved(entry.getKey()); - mediaDataManager.onNotificationRemoved(entry.getKey()); + removeEntry(entry); + } + }); + + // Pending entries are never inflated, and will never generate a call to onEntryRemoved(). + // This can happen when notifications are added and canceled before inflation. Add this + // separate listener for cleanup, since media inflation occurs onPendingEntryAdded(). + notificationEntryManager.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryCleanUp(@NonNull NotificationEntry entry) { + removeEntry(entry); } }); @@ -248,6 +259,11 @@ public class NotificationMediaManager implements Dumpable { mPropertiesChangedListener); } + private void removeEntry(NotificationEntry entry) { + onNotificationRemoved(entry.getKey()); + mMediaDataManager.onNotificationRemoved(entry.getKey()); + } + /** * Check if a state should be considered actively playing * @param state a PlaybackState diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 6b8afffa065a..5bee9a762f6f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -486,6 +486,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } } + row.showAppOpsIcons(entry.mActiveAppOps); row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 84108b196d72..4b244bb18975 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -76,9 +76,14 @@ public class AppOpsCoordinator implements Coordinator { // extend the lifetime of foreground notification services to show for at least 5 seconds mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender); + // listen for new notifications to add appOps + mNotifPipeline.addCollectionListener(mNotifCollectionListener); + // filter out foreground service notifications that aren't necessary anymore mNotifPipeline.addPreGroupFilter(mNotifFilter); + // when appOps change, update any relevant notifications to update appOps for + mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged); } /** @@ -169,4 +174,82 @@ public class AppOpsCoordinator implements Coordinator { } } }; + + /** + * Adds appOps to incoming and updating notifications + */ + private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() { + @Override + public void onEntryAdded(NotificationEntry entry) { + tagAppOps(entry); + } + + @Override + public void onEntryUpdated(NotificationEntry entry) { + tagAppOps(entry); + } + + private void tagAppOps(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); + // note: requires that the ForegroundServiceController is updating their appOps first + ArraySet<Integer> activeOps = + mForegroundServiceController.getAppOps( + sbn.getUser().getIdentifier(), + sbn.getPackageName()); + + entry.mActiveAppOps.clear(); + if (activeOps != null) { + entry.mActiveAppOps.addAll(activeOps); + } + } + }; + + private void onAppOpsChanged(int code, int uid, String packageName, boolean active) { + mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active)); + } + + /** + * Update the appOp for the posted notification associated with the current foreground service + * + * @param code code for appOp to add/remove + * @param uid of user the notification is sent to + * @param packageName package that created the notification + * @param active whether the appOpCode is active or not + */ + private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) { + Assert.isMainThread(); + + int userId = UserHandle.getUserId(uid); + + // Update appOps of the app's posted notifications with standard layouts + final ArraySet<String> notifKeys = + mForegroundServiceController.getStandardLayoutKeys(userId, packageName); + if (notifKeys != null) { + boolean changed = false; + for (int i = 0; i < notifKeys.size(); i++) { + final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i)); + if (entry != null + && uid == entry.getSbn().getUid() + && packageName.equals(entry.getSbn().getPackageName())) { + if (active) { + changed |= entry.mActiveAppOps.add(code); + } else { + changed |= entry.mActiveAppOps.remove(code); + } + } + } + if (changed) { + mNotifFilter.invalidateList(); + } + } + } + + private NotificationEntry findNotificationEntryWithKey(String key) { + for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) { + if (entry.getKey().equals(key)) { + return entry; + } + } + return null; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java new file mode 100644 index 000000000000..28c53dc6d9b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.service.notification.StatusBarNotification; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.R; + +/** + * The guts of a notification revealed when performing a long press. + */ +public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent { + private static final String TAG = "AppOpsGuts"; + + private PackageManager mPm; + + private String mPkg; + private String mAppName; + private int mAppUid; + private StatusBarNotification mSbn; + private ArraySet<Integer> mAppOps; + private MetricsLogger mMetricsLogger; + private OnSettingsClickListener mOnSettingsClickListener; + private NotificationGuts mGutsContainer; + private UiEventLogger mUiEventLogger; + + private OnClickListener mOnOk = v -> { + mGutsContainer.closeControls(v, false); + }; + + public AppOpsInfo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public interface OnSettingsClickListener { + void onClick(View v, String pkg, int uid, ArraySet<Integer> ops); + } + + public void bindGuts(final PackageManager pm, + final OnSettingsClickListener onSettingsClick, + final StatusBarNotification sbn, + final UiEventLogger uiEventLogger, + ArraySet<Integer> activeOps) { + mPkg = sbn.getPackageName(); + mSbn = sbn; + mPm = pm; + mAppName = mPkg; + mOnSettingsClickListener = onSettingsClick; + mAppOps = activeOps; + mUiEventLogger = uiEventLogger; + + bindHeader(); + bindPrompt(); + bindButtons(); + + logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN); + mMetricsLogger = new MetricsLogger(); + mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true); + } + + private void bindHeader() { + // Package name + Drawable pkgicon = null; + ApplicationInfo info; + try { + info = mPm.getApplicationInfo(mPkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + mAppUid = mSbn.getUid(); + mAppName = String.valueOf(mPm.getApplicationLabel(info)); + pkgicon = mPm.getApplicationIcon(info); + } + } catch (PackageManager.NameNotFoundException e) { + // app is gone, just show package name and generic icon + pkgicon = mPm.getDefaultActivityIcon(); + } + ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); + ((TextView) findViewById(R.id.pkgname)).setText(mAppName); + } + + private void bindPrompt() { + final TextView prompt = findViewById(R.id.prompt); + prompt.setText(getPrompt()); + } + + private void bindButtons() { + View settings = findViewById(R.id.settings); + settings.setOnClickListener((View view) -> { + mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps); + }); + TextView ok = findViewById(R.id.ok); + ok.setOnClickListener(mOnOk); + ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate()); + } + + private String getPrompt() { + if (mAppOps == null || mAppOps.size() == 0) { + return ""; + } else if (mAppOps.size() == 1) { + if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { + return mContext.getString(R.string.appops_camera); + } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { + return mContext.getString(R.string.appops_microphone); + } else { + return mContext.getString(R.string.appops_overlay); + } + } else if (mAppOps.size() == 2) { + if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { + if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { + return mContext.getString(R.string.appops_camera_mic); + } else { + return mContext.getString(R.string.appops_camera_overlay); + } + } else { + return mContext.getString(R.string.appops_mic_overlay); + } + } else { + return mContext.getString(R.string.appops_camera_mic_overlay); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mGutsContainer != null && + event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + if (mGutsContainer.isExposed()) { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_opened_accessibility, mAppName)); + } else { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_closed_accessibility, mAppName)); + } + } + } + + @Override + public void setGutsParent(NotificationGuts guts) { + mGutsContainer = guts; + } + + @Override + public boolean willBeRemoved() { + return false; + } + + @Override + public boolean shouldBeSaved() { + return false; + } + + @Override + public boolean needsFalsingProtection() { + return false; + } + + @Override + public View getContentView() { + return this; + } + + @Override + public boolean handleCloseControls(boolean save, boolean force) { + logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_CLOSE); + if (mMetricsLogger != null) { + mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false); + } + return false; + } + + @Override + public int getActualHeight() { + return getHeight(); + } + + private void logUiEvent(NotificationAppOpsEvent event) { + if (mSbn != null) { + mUiEventLogger.logWithInstanceId(event, + mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId()); + } + } +} 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 8ead7bf2322f..94e12e82f850 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 @@ -239,6 +239,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mShowNoBackground; private ExpandableNotificationRow mNotificationParent; private OnExpandClickListener mOnExpandClickListener; + private View.OnClickListener mOnAppOpsClickListener; // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. @@ -1142,6 +1143,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView items.add(NotificationMenuRow.createPartialConversationItem(mContext)); items.add(NotificationMenuRow.createInfoItem(mContext)); items.add(NotificationMenuRow.createSnoozeItem(mContext)); + items.add(NotificationMenuRow.createAppOpsItem(mContext)); mMenuRow.setMenuItems(items); } if (existed) { @@ -1607,6 +1609,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView RowContentBindStage rowContentBindStage, OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, + OnAppOpsClickListener onAppOpsClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier) { @@ -1626,6 +1629,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mRowContentBindStage = rowContentBindStage; mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; + setAppOpsOnClickListener(onAppOpsClickListener); mFalsingManager = falsingManager; mStatusbarStateController = statusBarStateController; mPeopleNotificationIdentifier = peopleNotificationIdentifier; @@ -1682,6 +1686,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView requestLayout(); } + public void showAppOpsIcons(ArraySet<Integer> activeOps) { + if (mIsSummaryWithChildren) { + mChildrenContainer.showAppOpsIcons(activeOps); + } + mPrivateLayout.showAppOpsIcons(activeOps); + mPublicLayout.showAppOpsIcons(activeOps); + } + /** Sets the last time the notification being displayed audibly alerted the user. */ public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) { if (NotificationUtils.useNewInterruptionModel(mContext)) { @@ -1710,6 +1722,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently); } + public View.OnClickListener getAppOpsOnClickListener() { + return mOnAppOpsClickListener; + } + + void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) { + mOnAppOpsClickListener = v -> { + createMenu(); + NotificationMenuRowPlugin provider = getProvider(); + if (provider == null) { + return; + } + MenuItem menuItem = provider.getAppOpsMenuItem(mContext); + if (menuItem != null) { + l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem); + } + }; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index b132caf56265..7a6109d2ce78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -63,6 +63,7 @@ public class ExpandableNotificationRowController { private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; + private final ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private final NotificationGutsManager mNotificationGutsManager; private Runnable mOnDismissRunnable; private final FalsingManager mFalsingManager; @@ -100,6 +101,7 @@ public class ExpandableNotificationRowController { mStatusBarStateController = statusBarStateController; mNotificationGutsManager = notificationGutsManager; mOnDismissRunnable = onDismissRunnable; + mOnAppOpsClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; @@ -120,6 +122,7 @@ public class ExpandableNotificationRowController { mRowContentBindStage, mOnExpandClickListener, mMediaManager, + mOnAppOpsClickListener, mFalsingManager, mStatusBarStateController, mPeopleNotificationIdentifier 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 41a9b187129c..1f5b063b0aa2 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 @@ -1567,6 +1567,18 @@ public class NotificationContentView extends FrameLayout { return header; } + public void showAppOpsIcons(ArraySet<Integer> activeOps) { + if (mContractedChild != null) { + mContractedWrapper.showAppOpsIcons(activeOps); + } + if (mExpandedChild != null) { + mExpandedWrapper.showAppOpsIcons(activeOps); + } + if (mHeadsUpChild != null) { + mHeadsUpWrapper.showAppOpsIcons(activeOps); + } + } + /** Sets whether the notification being displayed audibly alerted the user. */ public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { if (mContractedChild != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index f543db74d91a..25a0ea515ea3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -42,6 +42,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -539,12 +541,21 @@ public class NotificationConversationInfo extends LinearLayout implements && Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0) == 1; + Drawable person = mIconFactory.getBaseIconDrawable(mShortcutInfo); + if (person == null) { + person = mContext.getDrawable(R.drawable.ic_person).mutate(); + TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent}); + int colorAccent = ta.getColor(0, 0); + ta.recycle(); + person.setTint(colorAccent); + } + PriorityOnboardingDialogController controller = mBuilderProvider.get() .setContext(mUserContext) .setView(onboardingView) .setIgnoresDnd(ignoreDnd) .setShowsAsBubble(showAsBubble) - .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo)) + .setIcon(person) .setBadge(mIconFactory.getAppBadge( mPackageName, UserHandle.getUserId(mSbn.getUid()))) .setOnSettingsClick(mOnConversationSettingsClickListener) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 3eed18a90836..24883f51a984 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -264,6 +264,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx try { if (gutsView instanceof NotificationSnooze) { initializeSnoozeView(row, (NotificationSnooze) gutsView); + } else if (gutsView instanceof AppOpsInfo) { + initializeAppOpsInfo(row, (AppOpsInfo) gutsView); } else if (gutsView instanceof NotificationInfo) { initializeNotificationInfo(row, (NotificationInfo) gutsView); } else if (gutsView instanceof NotificationConversationInfo) { @@ -301,6 +303,36 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } /** + * Sets up the {@link AppOpsInfo} inside the notification row's guts. + * + * @param row view to set up the guts for + * @param appOpsInfoView view to set up/bind within {@code row} + */ + private void initializeAppOpsInfo( + final ExpandableNotificationRow row, + AppOpsInfo appOpsInfoView) { + NotificationGuts guts = row.getGuts(); + StatusBarNotification sbn = row.getEntry().getSbn(); + UserHandle userHandle = sbn.getUser(); + PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, + userHandle.getIdentifier()); + + AppOpsInfo.OnSettingsClickListener onSettingsClick = + (View v, String pkg, int uid, ArraySet<Integer> ops) -> { + mUiEventLogger.logWithInstanceId( + NotificationAppOpsEvent.NOTIFICATION_APP_OPS_SETTINGS_CLICK, + sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId()); + mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS); + guts.resetFalsingCheck(); + startAppOpsSettingsActivity(pkg, uid, ops, row); + }; + if (!row.getEntry().mActiveAppOps.isEmpty()) { + appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, mUiEventLogger, + row.getEntry().mActiveAppOps); + } + } + + /** * Sets up the {@link NotificationInfo} inside the notification row's guts. * @param row view to set up the guts for * @param notificationInfoView view to set up/bind within {@code row} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index a167925a6358..5e1e3b255867 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -76,6 +76,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private Context mContext; private FrameLayout mMenuContainer; private NotificationMenuItem mInfoItem; + private MenuItem mAppOpsItem; private MenuItem mSnoozeItem; private ArrayList<MenuItem> mLeftMenuItems; private ArrayList<MenuItem> mRightMenuItems; @@ -137,6 +138,11 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } @Override + public MenuItem getAppOpsMenuItem(Context context) { + return mAppOpsItem; + } + + @Override public MenuItem getSnoozeMenuItem(Context context) { return mSnoozeItem; } @@ -258,6 +264,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl // Only show snooze for non-foreground notifications, and if the setting is on mSnoozeItem = createSnoozeItem(mContext); } + mAppOpsItem = createAppOpsItem(mContext); NotificationEntry entry = mParent.getEntry(); int personNotifType = mPeopleNotificationIdentifier .getPeopleNotificationType(entry.getSbn(), entry.getRanking()); @@ -273,6 +280,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl mRightMenuItems.add(mSnoozeItem); } mRightMenuItems.add(mInfoItem); + mRightMenuItems.add(mAppOpsItem); mLeftMenuItems.addAll(mRightMenuItems); populateMenuViews(); @@ -680,6 +688,14 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl R.drawable.ic_settings); } + static MenuItem createAppOpsItem(Context context) { + AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate( + R.layout.app_ops_info, null, false); + MenuItem info = new NotificationMenuItem(context, null, appOpsContent, + -1 /*don't show in slow swipe menu */); + return info; + } + private void addMenuView(MenuItem item, ViewGroup parent) { View menuView = item.getMenuView(); if (menuView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 86fc352f6fe9..c747a7c300b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row.wrapper; import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y; +import android.app.AppOpsManager; import android.app.Notification; import android.content.Context; import android.util.ArraySet; @@ -63,6 +64,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private TextView mHeaderText; private TextView mAppNameText; private ImageView mWorkProfileImage; + private View mCameraIcon; + private View mMicIcon; + private View mOverlayIcon; + private View mAppOps; private View mAudiblyAlertedIcon; private FrameLayout mIconContainer; @@ -103,6 +108,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } }, TRANSFORMING_VIEW_TITLE); resolveHeaderViews(); + addAppOpsOnClickListener(row); } protected void resolveHeaderViews() { @@ -113,6 +119,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); + mCameraIcon = mView.findViewById(com.android.internal.R.id.camera); + mMicIcon = mView.findViewById(com.android.internal.R.id.mic); + mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay); + mAppOps = mView.findViewById(com.android.internal.R.id.app_ops); mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon); if (mNotificationHeader != null) { mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd); @@ -120,6 +130,38 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } } + private void addAppOpsOnClickListener(ExpandableNotificationRow row) { + View.OnClickListener listener = row.getAppOpsOnClickListener(); + if (mNotificationHeader != null) { + mNotificationHeader.setAppOpsOnClickListener(listener); + } + if (mAppOps != null) { + mAppOps.setOnClickListener(listener); + } + } + + /** + * Shows or hides 'app op in use' icons based on app usage. + */ + @Override + public void showAppOpsIcons(ArraySet<Integer> appOps) { + if (appOps == null) { + return; + } + if (mOverlayIcon != null) { + mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) + ? View.VISIBLE : View.GONE); + } + if (mCameraIcon != null) { + mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA) + ? View.VISIBLE : View.GONE); + } + if (mMicIcon != null) { + mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO) + ? View.VISIBLE : View.GONE); + } + } + @Override public void onContentUpdated(ExpandableNotificationRow row) { super.onContentUpdated(row); @@ -243,6 +285,15 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); } + if (mCameraIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mCameraIcon); + } + if (mMicIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mMicIcon); + } + if (mOverlayIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon); + } if (mAudiblyAlertedIcon != null) { mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 4bf279444462..30080e3d8cc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -96,6 +96,14 @@ public abstract class NotificationViewWrapper implements TransformableView { public void onContentUpdated(ExpandableNotificationRow row) { } + /** + * Show a set of app opp icons in the layout. + * + * @param appOps which app ops to show + */ + public void showAppOpsIcons(ArraySet<Integer> appOps) { + } + public void onReinflated() { if (shouldClearBackgroundOnReapply()) { mBackgroundColor = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 5edcde1cd3c7..99691b710cc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -1302,6 +1302,20 @@ public class NotificationChildrenContainer extends ViewGroup { mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader); } + /** + * Show a set of app opp icons in the layout. + * + * @param appOps which app ops to show + */ + public void showAppOpsIcons(ArraySet<Integer> appOps) { + if (mNotificationHeaderWrapper != null) { + mNotificationHeaderWrapper.showAppOpsIcons(appOps); + } + if (mNotificationHeaderWrapperLowPriority != null) { + mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps); + } + } + public void setRecentlyAudiblyAlerted(boolean audiblyAlertedRecently) { if (mNotificationHeaderWrapper != null) { mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index ff7793d506fd..4699ace90bd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -167,7 +167,7 @@ class NotificationSectionsManager @Inject internal constructor( peopleHubSubscription = null peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip) .apply { - setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() }) + setOnHeaderClickListener(View.OnClickListener { onPeopleHeaderClick() }) } if (ENABLE_SNOOZED_CONVERSATION_HUB) { peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary) @@ -522,6 +522,15 @@ class NotificationSectionsManager @Inject internal constructor( Intent.FLAG_ACTIVITY_SINGLE_TOP) } + private fun onPeopleHeaderClick() { + val intent = Intent(Settings.ACTION_CONVERSATION_SETTINGS) + activityStarter.startActivity( + intent, + true, + true, + Intent.FLAG_ACTIVITY_SINGLE_TOP) + } + private fun onClearGentleNotifsClick(v: View) { onClearSilentNotifsClickListener?.onClick(v) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 541c7845a5d3..744733597db4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -29,6 +29,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.FooterView; @@ -687,15 +688,27 @@ public class StackScrollAlgorithm { AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); float childrenOnTop = 0.0f; + + int topHunIndex = -1; + for (int i = 0; i < childCount; i++) { + ExpandableView child = algorithmState.visibleChildren.get(i); + if (child instanceof ActivatableNotificationView + && (child.isAboveShelf() || child.showingPulsing())) { + topHunIndex = i; + break; + } + } + for (int i = childCount - 1; i >= 0; i--) { childrenOnTop = updateChildZValue(i, childrenOnTop, - algorithmState, ambientState); + algorithmState, ambientState, i == topHunIndex); } } protected float updateChildZValue(int i, float childrenOnTop, StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState, + boolean shouldElevateHun) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableViewState childViewState = child.getViewState(); int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements(); @@ -713,8 +726,7 @@ public class StackScrollAlgorithm { } childViewState.zTranslation = baseZ + childrenOnTop * zDistanceBetweenElements; - } else if (child == ambientState.getTrackedHeadsUpRow() - || (i == 0 && (child.isAboveShelf() || child.showingPulsing()))) { + } else if (shouldElevateHun) { // In case this is a new view that has never been measured before, we don't want to // elevate if we are currently expanded more then the notification int shelfHeight = ambientState.getShelf() == null ? 0 : diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 304fe0090e77..00a932cb1e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -231,7 +231,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa } } - Dependency.get(ProtoTracer.class).add(this); mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT, ViewConfiguration.getLongPressTimeout()); @@ -286,6 +285,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa */ public void onNavBarAttached() { mIsAttached = true; + Dependency.get(ProtoTracer.class).add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); updateIsEnabled(); startTracking(); @@ -296,6 +296,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa */ public void onNavBarDetached() { mIsAttached = false; + Dependency.get(ProtoTracer.class).remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); updateIsEnabled(); stopTracking(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 93d4a5e93cb5..5bb3c836586c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -113,7 +113,8 @@ public class NavigationBarView extends FrameLayout implements int mNavigationIconHints = 0; private int mNavBarMode; - private final Region mActiveRegion = new Region(); + private final Region mTmpRegion = new Region(); + private final int[] mTmpPosition = new int[2]; private Rect mTmpBounds = new Rect(); private KeyButtonDrawable mBackIcon; @@ -252,24 +253,14 @@ public class NavigationBarView extends FrameLayout implements private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> { // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully // gestural mode, the entire nav bar should be touchable. - if (!mEdgeBackGestureHandler.isHandlingGestures() || mImeVisible) { + if (!mEdgeBackGestureHandler.isHandlingGestures()) { info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); return; } info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - ButtonDispatcher imeSwitchButton = getImeSwitchButton(); - if (imeSwitchButton.getVisibility() == VISIBLE) { - // If the IME is not up, but the ime switch button is visible, then make sure that - // button is touchable - int[] loc = new int[2]; - View buttonView = imeSwitchButton.getCurrentView(); - buttonView.getLocationInWindow(loc); - info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(), - loc[1] + buttonView.getHeight()); - return; - } - info.touchableRegion.setEmpty(); + info.touchableRegion.set(getButtonLocations(false /* includeFloatingRotationButton */, + false /* inScreen */)); }; private final Consumer<Boolean> mRotationButtonListener = (visible) -> { @@ -947,30 +938,44 @@ public class NavigationBarView extends FrameLayout implements * Notifies the overview service of the active touch regions. */ public void notifyActiveTouchRegions() { - mActiveRegion.setEmpty(); - updateButtonLocation(getBackButton()); - updateButtonLocation(getHomeButton()); - updateButtonLocation(getRecentsButton()); - updateButtonLocation(getImeSwitchButton()); - updateButtonLocation(getAccessibilityButton()); - if (mFloatingRotationButton.isVisible()) { - View floatingRotationView = mFloatingRotationButton.getCurrentView(); - floatingRotationView.getBoundsOnScreen(mTmpBounds); - mActiveRegion.op(mTmpBounds, Op.UNION); + mOverviewProxyService.onActiveNavBarRegionChanges( + getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */)); + } + + private Region getButtonLocations(boolean includeFloatingRotationButton, + boolean inScreenSpace) { + mTmpRegion.setEmpty(); + updateButtonLocation(getBackButton(), inScreenSpace); + updateButtonLocation(getHomeButton(), inScreenSpace); + updateButtonLocation(getRecentsButton(), inScreenSpace); + updateButtonLocation(getImeSwitchButton(), inScreenSpace); + updateButtonLocation(getAccessibilityButton(), inScreenSpace); + if (includeFloatingRotationButton && mFloatingRotationButton.isVisible()) { + updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace); } else { - updateButtonLocation(getRotateSuggestionButton()); + updateButtonLocation(getRotateSuggestionButton(), inScreenSpace); } - mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion); + return mTmpRegion; } - private void updateButtonLocation(ButtonDispatcher button) { + private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace) { View view = button.getCurrentView(); if (view == null || !button.isVisible()) { return; } + updateButtonLocation(view, inScreenSpace); + } - view.getBoundsOnScreen(mTmpBounds); - mActiveRegion.op(mTmpBounds, Op.UNION); + private void updateButtonLocation(View view, boolean inScreenSpace) { + if (inScreenSpace) { + view.getBoundsOnScreen(mTmpBounds); + } else { + view.getLocationInWindow(mTmpPosition); + mTmpBounds.set(mTmpPosition[0], mTmpPosition[1], + mTmpPosition[0] + view.getWidth(), + mTmpPosition[1] + view.getHeight()); + } + mTmpRegion.op(mTmpBounds, Op.UNION); } private void updateOrientationViews() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index c43ad36d4462..82ad00ad7c6d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Notification; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; @@ -359,6 +360,11 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { return false; } + private static boolean isOngoingCallNotif(NotificationEntry entry) { + return entry.getSbn().isOngoing() && Notification.CATEGORY_CALL.equals( + entry.getSbn().getNotification().category); + } + /** * This represents a notification and how long it is in a heads up mode. It also manages its * lifecycle automatically when created. @@ -391,6 +397,15 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { return 1; } + boolean selfCall = isOngoingCallNotif(mEntry); + boolean otherCall = isOngoingCallNotif(headsUpEntry.mEntry); + + if (selfCall && !otherCall) { + return -1; + } else if (!selfCall && otherCall) { + return 1; + } + if (remoteInputActive && !headsUpEntry.remoteInputActive) { return -1; } else if (!remoteInputActive && headsUpEntry.remoteInputActive) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java index 23d03a4b225a..e1ff9a236b02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java @@ -172,6 +172,24 @@ public class KeyButtonDrawable extends Drawable { } @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (changed) { + // End any existing animations when the visibility changes + jumpToCurrentState(); + } + return changed; + } + + @Override + public void jumpToCurrentState() { + super.jumpToCurrentState(); + if (mAnimatedDrawable != null) { + mAnimatedDrawable.jumpToCurrentState(); + } + } + + @Override public void setAlpha(int alpha) { mState.mAlpha = alpha; mIconPaint.setAlpha(alpha); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java index 2d8784dc41bd..9e1485dba8bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java @@ -225,6 +225,16 @@ public class KeyButtonRipple extends Drawable { } @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (changed) { + // End any existing animations when the visibility changes + jumpToCurrentState(); + } + return changed; + } + + @Override public void jumpToCurrentState() { endAnimations("jumpToCurrentState", false /* cancel */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index cf83603997c0..eb2d9bce6c4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -418,6 +418,10 @@ public class MobileSignalController extends SignalController< return (mServiceState != null && mServiceState.isEmergencyOnly()); } + public boolean isInService() { + return Utils.isInService(mServiceState); + } + private boolean isRoaming() { // During a carrier change, roaming indications need to be supressed. if (isCarrierNetworkChangeActive()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 95a97729936b..1dbb228f58b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -37,6 +37,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D DataUsageController getMobileDataController(); DataSaverController getDataSaverController(); String getMobileDataNetworkName(); + boolean isMobileDataNetworkInService(); int getNumberSubscriptions(); boolean hasVoiceCallingFeature(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index f41a27cf4c64..5b3c3a38c568 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -445,6 +445,12 @@ public class NetworkControllerImpl extends BroadcastReceiver } @Override + public boolean isMobileDataNetworkInService() { + MobileSignalController controller = getDataController(); + return controller != null && controller.isInService(); + } + + @Override public int getNumberSubscriptions() { return mMobileSignalControllers.size(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 2512257fdc19..9ad2aa257aa0 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -63,8 +63,7 @@ public class TunerServiceImpl extends TunerService { private static final String[] RESET_BLACKLIST = new String[] { QSTileHost.TILES_SETTING, Settings.Secure.DOZE_ALWAYS_ON, - Settings.Secure.MEDIA_CONTROLS_RESUME, - Secure.MEDIA_CONTROLS_RESUME_BLOCKED + Settings.Secure.MEDIA_CONTROLS_RESUME }; private final Observer mObserver = new Observer(); diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 21f7a85b3277..9e3fcc401ffa 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -21,15 +21,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.provider.Settings; -import android.text.TextUtils; import android.view.View; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.function.Consumer; public class Utils { @@ -144,21 +141,4 @@ public class Utils { Settings.Secure.MEDIA_CONTROLS_RESUME, 1); return useQsMediaPlayer(context) && flag > 0; } - - /** - * Get the set of apps for which the user has manually disabled resumption. - */ - public static Set<String> getBlockedMediaApps(Context context) { - String list = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED); - if (TextUtils.isEmpty(list)) { - return new HashSet<>(); - } - String[] names = list.split(":"); - Set<String> apps = new HashSet<>(names.length); - for (String s : names) { - apps.add(s); - } - return apps; - } } diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 89f469a438a9..dd4ea578dafe 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -228,12 +228,22 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mHandler.post(() -> { final Point lastSurfacePosition = mImeSourceControl != null ? mImeSourceControl.getSurfacePosition() : null; + final boolean positionChanged = + !activeControl.getSurfacePosition().equals(lastSurfacePosition); + final boolean leashChanged = + !haveSameLeash(mImeSourceControl, activeControl); mImeSourceControl = activeControl; - if (!activeControl.getSurfacePosition().equals(lastSurfacePosition) - && mAnimation != null) { - startAnimation(mImeShowing, true /* forceRestart */); - } else if (!mImeShowing) { - removeImeSurface(); + if (mAnimation != null) { + if (positionChanged) { + startAnimation(mImeShowing, true /* forceRestart */); + } + } else { + if (leashChanged) { + applyVisibilityToLeash(); + } + if (!mImeShowing) { + removeImeSurface(); + } } }); } @@ -241,6 +251,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + private void applyVisibilityToLeash() { + SurfaceControl leash = mImeSourceControl.getLeash(); + if (leash != null) { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + if (mImeShowing) { + t.show(leash); + } else { + t.hide(leash); + } + t.apply(); + mTransactionPool.release(t); + } + } + @Override public void showInsets(int types, boolean fromIme) { if ((types & WindowInsets.Type.ime()) == 0) { @@ -492,4 +516,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return IInputMethodManager.Stub.asInterface( ServiceManager.getService(Context.INPUT_METHOD_SERVICE)); } + + private static boolean haveSameLeash(InsetsSourceControl a, InsetsSourceControl b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a.getLeash() == b.getLeash()) { + return true; + } + if (a.getLeash() == null || b.getLeash() == null) { + return false; + } + return a.getLeash().isSameSurface(b.getLeash()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index e967a5d607eb..60f0cd9da5f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -82,7 +82,8 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { allowTestableLooperAsMainThread(); MockitoAnnotations.initMocks(this); - mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler); + mFsc = new ForegroundServiceController( + mEntryManager, mAppOpsController, mMainHandler); mListener = new ForegroundServiceNotificationListener( mContext, mFsc, mEntryManager, mNotifPipeline, mock(ForegroundServiceLifetimeExtender.class), mClock); @@ -114,6 +115,85 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } @Test + public void testAppOps_appOpChangedBeforeNotificationExists() { + // GIVEN app op exists, but notification doesn't exist in NEM yet + NotificationEntry entry = createFgEntry(); + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // WHEN the notification is added + mEntryListener.onPendingEntryAdded(entry); + + // THEN the app op is added to the entry + Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + } + + @Test + public void testAppOps_appOpAddedToForegroundNotif() { + // GIVEN a notification associated with a foreground service + NotificationEntry entry = addFgEntry(); + when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); + + // WHEN we are notified of a new app op for this notification + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + + // THEN the app op is added to the entry + Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // THEN notification views are updated since the notification is visible + verify(mEntryManager, times(1)).updateNotifications(anyString()); + } + + @Test + public void testAppOpsAlreadyAdded() { + // GIVEN a foreground service associated notification that already has the correct app op + NotificationEntry entry = addFgEntry(); + entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA); + when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); + + // WHEN we are notified of the same app op for this notification + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + + // THEN the app op still exists in the notification entry + Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // THEN notification views aren't updated since nothing changed + verify(mEntryManager, never()).updateNotifications(anyString()); + } + + @Test + public void testAppOps_appOpNotAddedToUnrelatedNotif() { + // GIVEN no notification entries correspond to the newly updated appOp + NotificationEntry entry = addFgEntry(); + when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null); + + // WHEN a new app op is detected + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + + // THEN we won't see appOps on the entry + Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // THEN notification views aren't updated since nothing changed + verify(mEntryManager, never()).updateNotifications(anyString()); + } + + @Test public void testAppOpsCRUD() { // no crash on remove that doesn't exist mFsc.onAppOpChanged(9, 1000, "pkg1", false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 8f082c15df36..ade329011b7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -47,6 +47,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import org.junit.Before; @@ -82,6 +83,8 @@ public class AppOpsControllerTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock(stubOnly = true) private AudioManager mAudioManager; + @Mock() + private BroadcastDispatcher mDispatcher; @Mock(stubOnly = true) private AudioManager.AudioRecordingCallback mRecordingCallback; @Mock(stubOnly = true) @@ -120,7 +123,8 @@ public class AppOpsControllerTest extends SysuiTestCase { mTestableLooper.getLooper(), mDumpManager, mFlagsCache, - mAudioManager + mAudioManager, + mDispatcher ); } @@ -128,12 +132,14 @@ public class AppOpsControllerTest extends SysuiTestCase { public void testOnlyListenForFewOps() { mController.setListening(true); verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController); + verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any()); } @Test public void testStopListening() { mController.setListening(false); verify(mAppOpsManager, times(1)).stopWatchingActive(mController); + verify(mDispatcher, times(1)).unregisterReceiver(mController); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 15828b41cc7c..1ad88560bf9c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -999,6 +999,29 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); } + + /** + * Verifies that when a non visually interruptive update occurs for a bubble in the overflow, + * the that bubble does not get promoted from the overflow. + */ + @Test + public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() { + // Setup + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mRow2.getEntry()); + assertTrue(mBubbleController.hasBubbles()); + + // Overflow it + mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(), + BubbleController.DISMISS_USER_GESTURE); + assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); + assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue(); + + // Test + mBubbleController.updateBubble(mRow.getEntry()); + assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); + } + /** * Sets the bubble metadata flags for this entry. These ]flags are normally set by * NotificationManagerService when the notification is sent, however, these tests do not diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 315caeebe0e9..4bbc41e517b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -513,6 +513,26 @@ public class BubbleDataTest extends SysuiTestCase { } /** + * Verifies that when a non visually interruptive update occurs, that the selection does not + * change. + */ + @Test + public void test_notVisuallyInterruptive_updateBubble_selectionDoesntChange() { + // Setup + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1] + mBubbleData.setListener(mListener); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2); + + // Test + sendUpdatedEntryAtTime(mEntryB1, 5000, false /* isVisuallyInterruptive */); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2); + } + + /** * Verifies that a request to expand the stack has no effect if there are no bubbles. */ @Test @@ -883,9 +903,15 @@ public class BubbleDataTest extends SysuiTestCase { } private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) { + sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */); + } + + private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime, + boolean visuallyInterruptive) { setPostTime(entry, postTime); // BubbleController calls this: Bubble b = mBubbleData.getOrCreateBubble(entry, null /* persistedBubble */); + b.setVisuallyInterruptiveForTest(visuallyInterruptive); // And then this mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/, true /* showInShade */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt index 861c6207f5b0..690b9a7248be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -78,4 +79,15 @@ class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() { assertEquals(list, wrapper.readFavorites()) } + + @Test + fun testSaveEmptyOnNonExistingFile() { + if (file.exists()) { + file.delete() + } + + wrapper.storeFavorites(emptyList()) + + assertFalse(file.exists()) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 3ef60274cd76..c82bb9bbd37c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -103,8 +103,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testAod_usesLightSensor() { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); mSensor.sendSensorEvent(3); @@ -114,8 +112,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testAod_usesDebugValue() throws Exception { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.onScreenState(Display.STATE_DOZE); Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS); intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1); @@ -166,14 +163,13 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void testDozingAfterPulsing_pausesLightSensor() throws Exception { + public void testScreenOffAfterPulsing_pausesLightSensor() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING); mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE); mScreen.transitionTo(DOZE_PULSE_DONE, DOZE); - mScreen.onScreenState(Display.STATE_DOZE); mSensor.sendSensorEvent(1); @@ -181,6 +177,17 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.onScreenState(Display.STATE_DOZE); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mSensor.sendSensorEvent(1); + + assertEquals(1, mServiceFake.screenBrightness); + } + + @Test public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, @@ -191,12 +198,15 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); + mScreen.onScreenState(Display.STATE_DOZE); + mScreen.onScreenState(Display.STATE_OFF); } @Test public void testNoBrightnessDeliveredAfterFinish() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.onScreenState(Display.STATE_DOZE); mScreen.transitionTo(DOZE_AOD, FINISH); mSensor.sendSensorEvent(1); diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index ac8c6710e041..5e2e7c075043 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -49,6 +49,7 @@ import android.testing.TestableLooper; import android.util.FeatureFlagUtils; import android.view.IWindowManager; import android.view.View; +import android.view.WindowManagerPolicyConstants; import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -241,6 +242,28 @@ public class GlobalActionsDialogTest extends SysuiTestCase { verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS); } + @Test + public void testShouldShowScreenshot() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.integer.config_navBarInteractionMode, + WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON); + + GlobalActionsDialog.ScreenshotAction screenshotAction = + mGlobalActionsDialog.makeScreenshotActionForTesting(); + assertThat(screenshotAction.shouldShow()).isTrue(); + } + + @Test + public void testShouldNotShowScreenshot() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.integer.config_navBarInteractionMode, + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON); + + GlobalActionsDialog.ScreenshotAction screenshotAction = + mGlobalActionsDialog.makeScreenshotActionForTesting(); + assertThat(screenshotAction.shouldShow()).isFalse(); + } + private void verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent event) { mTestableLooper.processAllMessages(); verify(mUiEventLogger, times(1)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 8a30b00e609d..81139f192070 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -43,7 +43,6 @@ import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import dagger.Lazy @@ -53,7 +52,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock import org.mockito.Mockito.anyBoolean @@ -203,7 +201,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindWhenUnattached() { val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, null, null, device, true, null) - player.bind(state) + player.bind(state, PACKAGE) assertThat(player.isPlaying()).isFalse() } @@ -212,7 +210,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player.attach(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null) - player.bind(state) + player.bind(state, PACKAGE) assertThat(appName.getText()).isEqualTo(APP) assertThat(titleText.getText()).isEqualTo(TITLE) assertThat(artistText.getText()).isEqualTo(ARTIST) @@ -223,7 +221,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player.attach(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null) - player.bind(state) + player.bind(state, PACKAGE) val list = ArgumentCaptor.forClass(ColorStateList::class.java) verify(view).setBackgroundTintList(list.capture()) assertThat(list.value).isEqualTo(ColorStateList.valueOf(BG_COLOR)) @@ -234,7 +232,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player.attach(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null) - player.bind(state) + player.bind(state, PACKAGE) assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isTrue() } @@ -246,7 +244,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player.attach(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null) - player.bind(state) + player.bind(state, PACKAGE) verify(expandedSet).setVisibility(seamless.id, View.GONE) verify(expandedSet).setVisibility(seamlessFallback.id, View.VISIBLE) verify(collapsedSet).setVisibility(seamless.id, View.GONE) @@ -258,7 +256,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player.attach(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) - player.bind(state) + player.bind(state, PACKAGE) assertThat(seamless.isEnabled()).isTrue() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( com.android.internal.R.string.ext_media_seamless_action)) @@ -270,7 +268,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null, resumption = true) - player.bind(state) + player.bind(state, PACKAGE) assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isFalse() } @@ -322,31 +320,18 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun dismissButtonClick() { + val mediaKey = "key for dismissal" player.attach(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null, notificationKey = KEY) - player.bind(state) + player.bind(state, mediaKey) dismiss.callOnClick() val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java) verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean()) captor.value.onDismiss() - verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong()) - } - - @Test - fun dismissButtonClick_nullNotificationKey() { - player.attach(holder) - val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) - player.bind(state) - - verify(keyguardDismissUtil, never()) - .executeWhenUnlocked( - any(ActivityStarter.OnDismissAction::class.java), - ArgumentMatchers.anyBoolean() - ) + verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index 492b33e3c4a6..609b8474d134 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -34,19 +33,23 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.ArrayList; -import java.util.Map; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class MediaDataCombineLatestTest extends SysuiTestCase { + @Rule public MockitoRule mockito = MockitoJUnit.rule(); + private static final String KEY = "TEST_KEY"; private static final String OLD_KEY = "TEST_KEY_OLD"; private static final String APP = "APP"; @@ -59,39 +62,26 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { private MediaDataCombineLatest mManager; - @Mock private MediaDataManager mDataSource; - @Mock private MediaDeviceManager mDeviceSource; @Mock private MediaDataManager.Listener mListener; - private MediaDataManager.Listener mDataListener; - private MediaDeviceManager.Listener mDeviceListener; - private MediaData mMediaData; private MediaDeviceData mDeviceData; @Before public void setUp() { - mDataSource = mock(MediaDataManager.class); - mDeviceSource = mock(MediaDeviceManager.class); - mListener = mock(MediaDataManager.Listener.class); - - mManager = new MediaDataCombineLatest(mDataSource, mDeviceSource); - - mDataListener = captureDataListener(); - mDeviceListener = captureDeviceListener(); - + mManager = new MediaDataCombineLatest(); mManager.addListener(mListener); mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, - new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, false, - KEY, false); + new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true, + false, KEY, false, false, false); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } @Test public void eventNotEmittedWithoutDevice() { // WHEN data source emits an event without device data - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -99,7 +89,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutMedia() { // WHEN device source emits an event without media data - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -107,9 +97,9 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterDeviceFirst() { // GIVEN that a device event has already been received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -119,9 +109,9 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterMediaFirst() { // GIVEN that media event has already been received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // WHEN device event is received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -131,11 +121,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyMediaFirst() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); @@ -145,11 +135,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyDeviceFirst() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); @@ -159,12 +149,12 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyMediaAfter() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); reset(mListener); // WHEN a second key migration event is received for media - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); // THEN the key has already been migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); @@ -174,12 +164,12 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyDeviceAfter() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); reset(mListener); // WHEN a second key migration event is received for the device - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the key has already be migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); @@ -189,60 +179,34 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemoved() { // WHEN media data is removed without first receiving device or data - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDataRemoved(KEY); // THEN a removed event isn't emitted verify(mListener, never()).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataRemovedAfterMediaEvent() { - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataRemovedAfterDeviceEvent() { - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed - mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); + mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); // THEN the listener gets a load event with the correct keys ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture()); } - - @Test - public void getDataIncludesDevice() { - // GIVEN that device and media events have been received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - - // THEN the result of getData includes device info - Map<String, MediaData> results = mManager.getData(); - assertThat(results.get(KEY)).isNotNull(); - assertThat(results.get(KEY).getDevice()).isEqualTo(mDeviceData); - } - - private MediaDataManager.Listener captureDataListener() { - ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass( - MediaDataManager.Listener.class); - verify(mDataSource).addListener(captor.capture()); - return captor.getValue(); - } - - private MediaDeviceManager.Listener captureDeviceListener() { - ArgumentCaptor<MediaDeviceManager.Listener> captor = ArgumentCaptor.forClass( - MediaDeviceManager.Listener.class); - verify(mDeviceSource).addListener(captor.capture()); - return captor.getValue(); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index afb64a7649b4..36b6527167f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -32,6 +32,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.concurrent.Executor @@ -56,8 +57,6 @@ private fun <T> any(): T = Mockito.any() class MediaDataFilterTest : SysuiTestCase() { @Mock - private lateinit var combineLatest: MediaDataCombineLatest - @Mock private lateinit var listener: MediaDataManager.Listener @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher @@ -78,8 +77,9 @@ class MediaDataFilterTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - mediaDataFilter = MediaDataFilter(combineLatest, broadcastDispatcher, mediaResumeListener, - mediaDataManager, lockscreenUserManager, executor) + mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener, + lockscreenUserManager, executor) + mediaDataFilter.mediaDataManager = mediaDataManager mediaDataFilter.addListener(listener) // Start all tests as main user @@ -152,8 +152,9 @@ class MediaDataFilterTest : SysuiTestCase() { @Test fun testOnUserSwitched_addsNewUserControls() { // GIVEN that we had some media for both users - val dataMap = mapOf(KEY to dataMain, KEY_ALT to dataGuest) - `when`(combineLatest.getData()).thenReturn(dataMap) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) + mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest) + reset(listener) // and we switch to guest user setUser(USER_GUEST) @@ -213,4 +214,4 @@ class MediaDataFilterTest : SysuiTestCase() { verify(mediaDataManager).setTimedOut(eq(KEY), eq(true)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 568dd6aafc3d..b47ee2921380 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -15,6 +15,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -23,9 +24,13 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever @@ -47,6 +52,7 @@ private fun <T> anyObject(): T { @RunWith(AndroidTestingRunner::class) class MediaDataManagerTest : SysuiTestCase() { + @JvmField @Rule val mockito = MockitoJUnit.rule() @Mock lateinit var mediaControllerFactory: MediaControllerFactory @Mock lateinit var controller: MediaController lateinit var session: MediaSession @@ -57,19 +63,36 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock lateinit var broadcastDispatcher: BroadcastDispatcher @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener @Mock lateinit var mediaResumeListener: MediaResumeListener + @Mock lateinit var mediaSessionBasedFilter: MediaSessionBasedFilter + @Mock lateinit var mediaDeviceManager: MediaDeviceManager + @Mock lateinit var mediaDataCombineLatest: MediaDataCombineLatest + @Mock lateinit var mediaDataFilter: MediaDataFilter + @Mock lateinit var listener: MediaDataManager.Listener @Mock lateinit var pendingIntent: PendingIntent - @JvmField @Rule val mockito = MockitoJUnit.rule() lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification + @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> @Before fun setup() { foregroundExecutor = FakeExecutor(FakeSystemClock()) backgroundExecutor = FakeExecutor(FakeSystemClock()) - mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor, - mediaControllerFactory, broadcastDispatcher, dumpManager, - mediaTimeoutListener, mediaResumeListener, useMediaResumption = true, - useQsMediaPlayer = true) + mediaDataManager = MediaDataManager( + context = context, + backgroundExecutor = backgroundExecutor, + foregroundExecutor = foregroundExecutor, + mediaControllerFactory = mediaControllerFactory, + broadcastDispatcher = broadcastDispatcher, + dumpManager = dumpManager, + mediaTimeoutListener = mediaTimeoutListener, + mediaResumeListener = mediaResumeListener, + mediaSessionBasedFilter = mediaSessionBasedFilter, + mediaDeviceManager = mediaDeviceManager, + mediaDataCombineLatest = mediaDataCombineLatest, + mediaDataFilter = mediaDataFilter, + useMediaResumption = true, + useQsMediaPlayer = true + ) session = MediaSession(context, "MediaDataManagerTestSession") mediaNotification = SbnBuilder().run { setPkg(PACKAGE_NAME) @@ -84,6 +107,12 @@ class MediaDataManagerTest : SysuiTestCase() { putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) } whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller) + + // This is an ugly hack for now. The mediaSessionBasedFilter is one of the internal + // listeners in the internal processing pipeline. It receives events, but ince it is a + // mock, it doesn't pass those events along the chain to the external listeners. So, just + // treat mediaSessionBasedFilter as a listener for testing. + listener = mediaSessionBasedFilter } @After @@ -113,8 +142,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_callsListener() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject()) @@ -122,136 +149,74 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_conservesActiveFlag() { - val listener = TestListener() whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - assertThat(listener.data!!.active).isTrue() + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value!!.active).isTrue() } @Test fun testOnNotificationRemoved_callsListener() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.onNotificationRemoved(KEY) - verify(listener).onMediaDataRemoved(eq(KEY)) } @Test fun testOnNotificationRemoved_withResumption() { // GIVEN that the manager has a notification with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed mediaDataManager.onNotificationRemoved(KEY) // THEN the media data indicates that it is for resumption - assertThat(listener.data!!.resumption).isTrue() - // AND the new key is the package name - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) - assertThat(listener.removedKey).isNull() + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() } @Test fun testOnNotificationRemoved_twoWithResumption() { // GIVEN that the manager has two notifications with resume actions - val listener = TestListener() - mediaDataManager.addListener(listener) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onNotificationAdded(KEY_2, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() val resumableData = data.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, resumableData) mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData) + reset(listener) // WHEN the first is removed mediaDataManager.onNotificationRemoved(KEY) // THEN the data is for resumption and the key is migrated to the package name - assertThat(listener.data!!.resumption).isTrue() - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) - assertThat(listener.removedKey).isNull() + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + verify(listener, never()).onMediaDataRemoved(eq(KEY)) // WHEN the second is removed mediaDataManager.onNotificationRemoved(KEY_2) // THEN the data is for resumption and the second key is removed - assertThat(listener.data!!.resumption).isTrue() - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.removedKey!!).isEqualTo(KEY_2) - } - - @Test - fun testAppBlockedFromResumption() { - // GIVEN that the manager has a notification with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! - assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) - - // and the manager should block the package from creating resume controls - val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") - mediaDataManager.appsBlockedFromResume = blocked - - // WHEN the notification is removed - mediaDataManager.onNotificationRemoved(KEY) - - // THEN the media data is removed - assertThat(listener.removedKey!!).isEqualTo(KEY) - } - - @Test - fun testAppUnblockedFromResumption() { - // GIVEN that an app was blocked from resuming - val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") - mediaDataManager.appsBlockedFromResume = blocked - - // and GIVEN that the manager has a notification from that app with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! - assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) - - // WHEN the app is unblocked - mediaDataManager.appsBlockedFromResume = mutableSetOf("com.example.app") - - // and the notification is removed - mediaDataManager.onNotificationRemoved(KEY) - - // THEN the entry will stay as a resume control - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(PACKAGE_NAME), + capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + verify(listener).onMediaDataRemoved(eq(KEY_2)) } @Test fun testAddResumptionControls() { - val listener = TestListener() - mediaDataManager.addListener(listener) // WHEN resumption controls are added` val desc = MediaDescription.Builder().run { setTitle(SESSION_TITLE) @@ -262,7 +227,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN the media data indicates that it is for resumption - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isTrue() assertThat(data.song).isEqualTo(SESSION_TITLE) assertThat(data.app).isEqualTo(APP_NAME) @@ -271,8 +237,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testDismissMedia_listenerCalled() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.dismissMediaData(KEY, 0L) @@ -282,26 +246,4 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } - - /** - * Simple implementation of [MediaDataManager.Listener] for the test. - * - * Giving up on trying to get a mock Listener and ArgumentCaptor to work. - */ - private class TestListener : MediaDataManager.Listener { - var data: MediaData? = null - var key: String? = null - var oldKey: String? = null - var removedKey: String? = null - - override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - this.key = key - this.oldKey = oldKey - this.data = data - } - - override fun onMediaDataRemoved(key: String) { - removedKey = key - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index 3c6e19f0ec6f..ab3b20898b23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -16,13 +16,12 @@ package com.android.systemui.media -import android.app.Notification import android.graphics.drawable.Drawable -import android.media.MediaMetadata import android.media.MediaRouter2Manager import android.media.RoutingSessionInfo +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo import android.media.session.MediaSession -import android.media.session.PlaybackState import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -55,7 +54,6 @@ private const val KEY = "TEST_KEY" private const val KEY_OLD = "TEST_KEY_OLD" private const val PACKAGE = "PKG" private const val SESSION_KEY = "SESSION_KEY" -private const val SESSION_ARTIST = "SESSION_ARTIST" private const val SESSION_TITLE = "SESSION_TITLE" private const val DEVICE_NAME = "DEVICE_NAME" private const val USER_ID = 0 @@ -68,28 +66,29 @@ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value public class MediaDeviceManagerTest : SysuiTestCase() { private lateinit var manager: MediaDeviceManager - @Mock private lateinit var mediaDataManager: MediaDataManager + @Mock private lateinit var controllerFactory: MediaControllerFactory @Mock private lateinit var lmmFactory: LocalMediaManagerFactory @Mock private lateinit var lmm: LocalMediaManager @Mock private lateinit var mr2: MediaRouter2Manager - private lateinit var fakeExecutor: FakeExecutor + private lateinit var fakeFgExecutor: FakeExecutor + private lateinit var fakeBgExecutor: FakeExecutor @Mock private lateinit var dumpster: DumpManager @Mock private lateinit var listener: MediaDeviceManager.Listener @Mock private lateinit var device: MediaDevice @Mock private lateinit var icon: Drawable @Mock private lateinit var route: RoutingSessionInfo + @Mock private lateinit var controller: MediaController + @Mock private lateinit var playbackInfo: PlaybackInfo private lateinit var session: MediaSession - private lateinit var metadataBuilder: MediaMetadata.Builder - private lateinit var playbackBuilder: PlaybackState.Builder - private lateinit var notifBuilder: Notification.Builder private lateinit var mediaData: MediaData @JvmField @Rule val mockito = MockitoJUnit.rule() @Before fun setUp() { - fakeExecutor = FakeExecutor(FakeSystemClock()) - manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager, - dumpster) + fakeFgExecutor = FakeExecutor(FakeSystemClock()) + fakeBgExecutor = FakeExecutor(FakeSystemClock()) + manager = MediaDeviceManager(controllerFactory, lmmFactory, mr2, fakeFgExecutor, + fakeBgExecutor, dumpster) manager.addListener(listener) // Configure mocks. @@ -100,28 +99,13 @@ public class MediaDeviceManagerTest : SysuiTestCase() { whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route) // Create a media sesssion and notification for testing. - metadataBuilder = MediaMetadata.Builder().apply { - putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) - putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) - } - playbackBuilder = PlaybackState.Builder().apply { - setState(PlaybackState.STATE_PAUSED, 6000L, 1f) - setActions(PlaybackState.ACTION_PLAY) - } - session = MediaSession(context, SESSION_KEY).apply { - setMetadata(metadataBuilder.build()) - setPlaybackState(playbackBuilder.build()) - } - session.setActive(true) - notifBuilder = Notification.Builder(context, "NONE").apply { - setContentTitle(SESSION_TITLE) - setContentText(SESSION_ARTIST) - setSmallIcon(android.R.drawable.ic_media_pause) - setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken())) - } + session = MediaSession(context, SESSION_KEY) + mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null, emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null, device = null, active = true, resumeAction = null) + whenever(controllerFactory.create(session.sessionToken)) + .thenReturn(controller) } @After @@ -144,13 +128,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun loadAndRemoveMediaData() { manager.onMediaDataLoaded(KEY, null, mediaData) manager.onMediaDataRemoved(KEY) + fakeBgExecutor.runAllReady() verify(lmm).unregisterCallback(any()) } @Test fun loadMediaDataWithNullToken() { manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null)) - fakeExecutor.runAllReady() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() assertThat(data.name).isEqualTo(DEVICE_NAME) @@ -163,6 +149,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { reset(listener) // WHEN data is loaded with a new key manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the listener for the old key should removed. verify(lmm).unregisterCallback(any()) // AND a new device event emitted @@ -186,6 +174,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun unknownOldKey() { val oldKey = "unknown" manager.onMediaDataLoaded(KEY, oldKey, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any()) } @@ -193,13 +183,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun updateToSessionTokenWithNullRoute() { // GIVEN that media data has been loaded with a null token manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null)) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) // WHEN media data is loaded with a different token // AND that token results in a null route - reset(listener) whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device should be disabled - fakeExecutor.runAllReady() val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() assertThat(data.name).isNull() @@ -210,7 +203,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun deviceEventOnAddNotification() { // WHEN a notification is added manager.onMediaDataLoaded(KEY, null, mediaData) - val deviceCallback = captureCallback() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the update is dispatched to the listener val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() @@ -230,10 +224,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun deviceListUpdate() { manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() val deviceCallback = captureCallback() // WHEN the device list changes deviceCallback.onDeviceListUpdate(mutableListOf(device)) - assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) // THEN the update is dispatched to the listener val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() @@ -244,10 +240,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun selectedDeviceStateChanged() { manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() val deviceCallback = captureCallback() // WHEN the selected device changes state deviceCallback.onSelectedDeviceStateChanged(device, 1) - assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) // THEN the update is dispatched to the listener val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() @@ -270,6 +268,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) // WHEN a notification is added manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device is disabled val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() @@ -281,13 +281,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() { // GIVEN a notif is added manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() reset(listener) // AND MR2Manager returns null for routing session whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) // WHEN the selected device changes state val deviceCallback = captureCallback() deviceCallback.onSelectedDeviceStateChanged(device, 1) - fakeExecutor.runAllReady() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device is disabled val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() @@ -299,13 +302,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() { // GIVEN a notif is added manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() reset(listener) // GIVEN that MR2Manager returns null for routing session whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) // WHEN the selected device changes state val deviceCallback = captureCallback() deviceCallback.onDeviceListUpdate(mutableListOf(device)) - fakeExecutor.runAllReady() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device is disabled val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() @@ -313,6 +319,41 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.icon).isNull() } + @Test + fun audioInfoChanged() { + whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL) + whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo) + // GIVEN a controller with local playback type + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(mr2) + // WHEN onAudioInfoChanged fires with remote playback type + whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java) + verify(controller).registerCallback(captor.capture()) + captor.value.onAudioInfoChanged(playbackInfo) + // THEN the route is checked + verify(mr2).getRoutingSessionForMediaController(eq(controller)) + } + + @Test + fun audioInfoHasntChanged() { + whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo) + // GIVEN a controller with remote playback type + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(mr2) + // WHEN onAudioInfoChanged fires with remote playback type + val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java) + verify(controller).registerCallback(captor.capture()) + captor.value.onAudioInfoChanged(playbackInfo) + // THEN the route is not checked + verify(mr2, never()).getRoutingSessionForMediaController(eq(controller)) + } + fun captureCallback(): LocalMediaManager.DeviceCallback { val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java) verify(lmm).registerCallback(captor.capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt new file mode 100644 index 000000000000..00b003d290ee --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock + +@SmallTest +@RunWith(AndroidTestingRunner::class) +public class MediaPlayerDataTest : SysuiTestCase() { + + companion object { + val LOCAL = true + val RESUMPTION = true + val PLAYING = true + val UNDETERMINED = null + } + + @Before + fun setup() { + MediaPlayerData.clear() + } + + @Test + fun addPlayingThenRemote() { + val playerIsPlaying = mock(MediaControlPanel::class.java) + val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION) + + val playerIsRemote = mock(MediaControlPanel::class.java) + val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote) + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(2) + assertThat(players).containsExactly(playerIsPlaying, playerIsRemote).inOrder() + } + + @Test + fun switchPlayersPlaying() { + val playerIsPlaying1 = mock(MediaControlPanel::class.java) + var dataIsPlaying1 = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION) + + val playerIsPlaying2 = mock(MediaControlPanel::class.java) + var dataIsPlaying2 = createMediaData("app2", !PLAYING, LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) + MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) + + dataIsPlaying1 = createMediaData("app1", !PLAYING, LOCAL, !RESUMPTION) + dataIsPlaying2 = createMediaData("app2", PLAYING, LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) + MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(2) + assertThat(players).containsExactly(playerIsPlaying2, playerIsPlaying1).inOrder() + } + + @Test + fun fullOrderTest() { + val playerIsPlaying = mock(MediaControlPanel::class.java) + val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION) + + val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java) + val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION) + + val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java) + val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION) + + val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java) + val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION) + + val playerCanResume = mock(MediaControlPanel::class.java) + val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION) + + val playerUndetermined = mock(MediaControlPanel::class.java) + val dataUndetermined = createMediaData("app6", UNDETERMINED, LOCAL, RESUMPTION) + + MediaPlayerData.addMediaPlayer("3", dataIsStoppedAndLocal, playerIsStoppedAndLocal) + MediaPlayerData.addMediaPlayer("5", dataIsStoppedAndRemote, playerIsStoppedAndRemote) + MediaPlayerData.addMediaPlayer("4", dataCanResume, playerCanResume) + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) + MediaPlayerData.addMediaPlayer("2", dataIsPlayingAndRemote, playerIsPlayingAndRemote) + MediaPlayerData.addMediaPlayer("6", dataUndetermined, playerUndetermined) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(6) + assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote, + playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote, + playerUndetermined).inOrder() + } + + private fun createMediaData( + app: String, + isPlaying: Boolean?, + isLocalSession: Boolean, + resumption: Boolean + ) = + MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "", + null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt new file mode 100644 index 000000000000..5d81de6bce00 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.content.pm.ServiceInfo +import android.graphics.Color +import android.media.MediaDescription +import android.media.session.MediaSession +import android.provider.Settings +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.tuner.TunerService +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import org.junit.After +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +private const val KEY = "TEST_KEY" +private const val OLD_KEY = "RESUME_KEY" +private const val APP = "APP" +private const val BG_COLOR = Color.RED +private const val PACKAGE_NAME = "PKG" +private const val CLASS_NAME = "CLASS" +private const val ARTIST = "ARTIST" +private const val TITLE = "TITLE" +private const val USER_ID = 0 +private const val MEDIA_PREFERENCES = "media_control_prefs" +private const val RESUME_COMPONENTS = "package1/class1:package2/class2:package3/class3" + +private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() +private fun <T> eq(value: T): T = Mockito.eq(value) ?: value +private fun <T> any(): T = Mockito.any<T>() + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class MediaResumeListenerTest : SysuiTestCase() { + + @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock private lateinit var mediaDataManager: MediaDataManager + @Mock private lateinit var device: MediaDeviceData + @Mock private lateinit var token: MediaSession.Token + @Mock private lateinit var tunerService: TunerService + @Mock private lateinit var resumeBrowserFactory: ResumeMediaBrowserFactory + @Mock private lateinit var resumeBrowser: ResumeMediaBrowser + @Mock private lateinit var sharedPrefs: SharedPreferences + @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor + @Mock private lateinit var mockContext: Context + @Mock private lateinit var pendingIntent: PendingIntent + + @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> + + private lateinit var executor: FakeExecutor + private lateinit var data: MediaData + private lateinit var resumeListener: MediaResumeListener + + private var originalQsSetting = Settings.Global.getInt(context.contentResolver, + Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1) + private var originalResumeSetting = Settings.Secure.getInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, 0) + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + Settings.Global.putInt(context.contentResolver, + Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1) + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, 1) + + whenever(resumeBrowserFactory.create(capture(callbackCaptor), any())) + .thenReturn(resumeBrowser) + + // resume components are stored in sharedpreferences + whenever(mockContext.getSharedPreferences(eq(MEDIA_PREFERENCES), anyInt())) + .thenReturn(sharedPrefs) + whenever(sharedPrefs.getString(any(), any())).thenReturn(RESUME_COMPONENTS) + whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor) + whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor) + whenever(mockContext.packageManager).thenReturn(context.packageManager) + whenever(mockContext.contentResolver).thenReturn(context.contentResolver) + + executor = FakeExecutor(FakeSystemClock()) + resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, + tunerService, resumeBrowserFactory) + resumeListener.setManager(mediaDataManager) + mediaDataManager.addListener(resumeListener) + + data = MediaData( + userId = USER_ID, + initialized = true, + backgroundColor = BG_COLOR, + app = APP, + appIcon = null, + artist = ARTIST, + song = TITLE, + artwork = null, + actions = emptyList(), + actionsToShowInCompact = emptyList(), + packageName = PACKAGE_NAME, + token = token, + clickIntent = null, + device = device, + active = true, + notificationKey = KEY, + resumeAction = null) + } + + @After + fun tearDown() { + Settings.Global.putInt(context.contentResolver, + Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, originalQsSetting) + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, originalResumeSetting) + } + + @Test + fun testWhenNoResumption_doesNothing() { + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, 0) + + // When listener is created, we do NOT register a user change listener + val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService, + resumeBrowserFactory) + listener.setManager(mediaDataManager) + verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver), + any(), any(), any()) + + // When data is loaded, we do NOT execute or update anything + listener.onMediaDataLoaded(KEY, OLD_KEY, data) + assertThat(executor.numPending()).isEqualTo(0) + verify(mediaDataManager, never()).setResumeAction(any(), any()) + } + + @Test + fun testOnLoad_checksForResume_noService() { + // When media data is loaded that has not been checked yet, and does not have a MBS + resumeListener.onMediaDataLoaded(KEY, null, data) + + // Then we report back to the manager + verify(mediaDataManager).setResumeAction(KEY, null) + } + + @Test + fun testOnLoad_checksForResume_hasService() { + // Set up mocks to successfully find a MBS that returns valid media + val pm = mock(PackageManager::class.java) + whenever(mockContext.packageManager).thenReturn(pm) + val resolveInfo = ResolveInfo() + val serviceInfo = ServiceInfo() + serviceInfo.packageName = PACKAGE_NAME + resolveInfo.serviceInfo = serviceInfo + resolveInfo.serviceInfo.name = CLASS_NAME + val resumeInfo = listOf(resolveInfo) + whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo) + + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.testConnection()).thenAnswer { + callbackCaptor.value.addTrack(description, component, resumeBrowser) + } + + // When media data is loaded that has not been checked yet, and does have a MBS + val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false) + resumeListener.onMediaDataLoaded(KEY, null, dataCopy) + + // Then we test whether the service is valid + executor.runAllReady() + verify(resumeBrowser).testConnection() + + // And since it is, we report back to the manager + verify(mediaDataManager).setResumeAction(eq(KEY), any()) + + // But we do not tell it to add new controls + verify(mediaDataManager, never()) + .addResumptionControls(anyInt(), any(), any(), any(), any(), any(), any()) + + // Finally, make sure the resume browser disconnected + verify(resumeBrowser).disconnect() + } + + @Test + fun testOnLoad_doesNotCheckAgain() { + // When a media data is loaded that has been checked already + var dataCopy = data.copy(hasCheckedForResume = true) + resumeListener.onMediaDataLoaded(KEY, null, dataCopy) + + // Then we should not check it again + verify(resumeBrowser, never()).testConnection() + verify(mediaDataManager, never()).setResumeAction(KEY, null) + } + + @Test + fun testOnUserUnlock_loadsTracks() { + // Set up mock service to successfully find valid media + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.token).thenReturn(token) + whenever(resumeBrowser.appIntent).thenReturn(pendingIntent) + whenever(resumeBrowser.findRecentMedia()).thenAnswer { + callbackCaptor.value.addTrack(description, component, resumeBrowser) + } + + // Make sure broadcast receiver is registered + resumeListener.setManager(mediaDataManager) + verify(broadcastDispatcher).registerReceiver(eq(resumeListener.userChangeReceiver), + any(), any(), any()) + + // When we get an unlock event + val intent = Intent(Intent.ACTION_USER_UNLOCKED) + resumeListener.userChangeReceiver.onReceive(context, intent) + + // Then we should attempt to find recent media for each saved component + verify(resumeBrowser, times(3)).findRecentMedia() + + // Then since the mock service found media, the manager should be informed + verify(mediaDataManager, times(3)).addResumptionControls(anyInt(), + any(), any(), any(), any(), any(), eq(PACKAGE_NAME)) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt new file mode 100644 index 000000000000..2d90cc4f6712 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.graphics.Color +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.time.FakeSystemClock + +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever + +private const val PACKAGE = "PKG" +private const val KEY = "TEST_KEY" +private const val NOTIF_KEY = "TEST_KEY" +private const val SESSION_ARTIST = "SESSION_ARTIST" +private const val SESSION_TITLE = "SESSION_TITLE" +private const val APP_NAME = "APP_NAME" +private const val USER_ID = 0 + +private val info = MediaData( + userId = USER_ID, + initialized = true, + backgroundColor = Color.DKGRAY, + app = APP_NAME, + appIcon = null, + artist = SESSION_ARTIST, + song = SESSION_TITLE, + artwork = null, + actions = emptyList(), + actionsToShowInCompact = emptyList(), + packageName = PACKAGE, + token = null, + clickIntent = null, + device = null, + active = true, + resumeAction = null, + resumption = false, + notificationKey = NOTIF_KEY, + hasCheckedForResume = false +) + +private fun <T> eq(value: T): T = Mockito.eq(value) ?: value + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class MediaSessionBasedFilterTest : SysuiTestCase() { + + @JvmField @Rule val mockito = MockitoJUnit.rule() + + // Unit to be tested + private lateinit var filter: MediaSessionBasedFilter + + private lateinit var sessionListener: MediaSessionManager.OnActiveSessionsChangedListener + @Mock private lateinit var mediaListener: MediaDataManager.Listener + + // MediaSessionBasedFilter dependencies + @Mock private lateinit var mediaSessionManager: MediaSessionManager + private lateinit var fgExecutor: FakeExecutor + private lateinit var bgExecutor: FakeExecutor + + @Mock private lateinit var controller1: MediaController + @Mock private lateinit var controller2: MediaController + @Mock private lateinit var controller3: MediaController + @Mock private lateinit var controller4: MediaController + + private lateinit var token1: MediaSession.Token + private lateinit var token2: MediaSession.Token + private lateinit var token3: MediaSession.Token + private lateinit var token4: MediaSession.Token + + @Mock private lateinit var remotePlaybackInfo: PlaybackInfo + @Mock private lateinit var localPlaybackInfo: PlaybackInfo + + private lateinit var session1: MediaSession + private lateinit var session2: MediaSession + private lateinit var session3: MediaSession + private lateinit var session4: MediaSession + + private lateinit var mediaData1: MediaData + private lateinit var mediaData2: MediaData + private lateinit var mediaData3: MediaData + private lateinit var mediaData4: MediaData + + @Before + fun setUp() { + fgExecutor = FakeExecutor(FakeSystemClock()) + bgExecutor = FakeExecutor(FakeSystemClock()) + filter = MediaSessionBasedFilter(context, mediaSessionManager, fgExecutor, bgExecutor) + + // Configure mocks. + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(emptyList()) + + session1 = MediaSession(context, "MediaSessionBasedFilter1") + session2 = MediaSession(context, "MediaSessionBasedFilter2") + session3 = MediaSession(context, "MediaSessionBasedFilter3") + session4 = MediaSession(context, "MediaSessionBasedFilter4") + + token1 = session1.sessionToken + token2 = session2.sessionToken + token3 = session3.sessionToken + token4 = session4.sessionToken + + whenever(controller1.getSessionToken()).thenReturn(token1) + whenever(controller2.getSessionToken()).thenReturn(token2) + whenever(controller3.getSessionToken()).thenReturn(token3) + whenever(controller4.getSessionToken()).thenReturn(token4) + + whenever(controller1.getPackageName()).thenReturn(PACKAGE) + whenever(controller2.getPackageName()).thenReturn(PACKAGE) + whenever(controller3.getPackageName()).thenReturn(PACKAGE) + whenever(controller4.getPackageName()).thenReturn(PACKAGE) + + mediaData1 = info.copy(token = token1) + mediaData2 = info.copy(token = token2) + mediaData3 = info.copy(token = token3) + mediaData4 = info.copy(token = token4) + + whenever(remotePlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(localPlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL) + + whenever(controller1.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller2.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller3.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller4.getPlaybackInfo()).thenReturn(localPlaybackInfo) + + // Capture listener + bgExecutor.runAllReady() + val listenerCaptor = ArgumentCaptor.forClass( + MediaSessionManager.OnActiveSessionsChangedListener::class.java) + verify(mediaSessionManager).addOnActiveSessionsChangedListener( + listenerCaptor.capture(), any()) + sessionListener = listenerCaptor.value + + filter.addListener(mediaListener) + } + + @After + fun tearDown() { + session1.release() + session2.release() + session3.release() + session4.release() + } + + @Test + fun noMediaSession_loadedEventNotFiltered() { + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun noMediaSession_removedEventNotFiltered() { + filter.onMediaDataRemoved(KEY) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + verify(mediaListener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun matchingMediaSession_loadedEventNotFiltered() { + // GIVEN an active session + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun matchingMediaSession_removedEventNotFiltered() { + // GIVEN an active session + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a removed event is received + filter.onMediaDataRemoved(KEY) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun remoteSession_loadedEventNotFiltered() { + // GIVEN a remote session + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matche the session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun remoteAndLocalSessions_localLoadedEventFiltered() { + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) + } + + @Test + fun remoteAndLocalSessions_remoteSessionWithoutNotification() { + // GIVEN remote and local sessions + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered because there isn't a notification for the remote + // session. + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() { + // GIVEN remote and local sessions + val key1 = "KEY_1" + val key2 = "KEY_2" + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key1, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + // AND there should be a removed event for key2 + verify(mediaListener).onMediaDataRemoved(eq(key2)) + } + + @Test + fun remoteAndLocalHaveDifferentKeys_remoteSessionWithoutNotification() { + // GIVEN remote and local sessions + val key1 = "KEY_1" + val key2 = "KEY_2" + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key1, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + } + + @Test + fun multipleRemoteSessions_loadedEventNotFiltered() { + // GIVEN two remote sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) + } + + @Test + fun multipleOtherSessions_loadedEventNotFiltered() { + // GIVEN multiple active sessions from other packages + val controllers = listOf(controller1, controller2, controller3, controller4) + whenever(controller1.getPackageName()).thenReturn("PKG_1") + whenever(controller2.getPackageName()).thenReturn("PKG_2") + whenever(controller3.getPackageName()).thenReturn("PKG_3") + whenever(controller4.getPackageName()).thenReturn("PKG_4") + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun doNotFilterDuringKeyMigration() { + val key1 = "KEY_1" + val key2 = "KEY_2" + // GIVEN a loaded event + filter.onMediaDataLoaded(key1, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session but it is a key migration + filter.onMediaDataLoaded(key2, key1, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is fired + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2)) + } + + @Test + fun filterAfterKeyMigration() { + val key1 = "KEY_1" + val key2 = "KEY_2" + // GIVEN a loaded event + filter.onMediaDataLoaded(key1, null, mediaData1) + filter.onMediaDataLoaded(key1, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // GIVEN that the keys have been migrated + filter.onMediaDataLoaded(key2, key1, mediaData1) + filter.onMediaDataLoaded(key2, key1, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key2, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is fired + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt index 7a8e4f7e9b85..f3979592c894 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt @@ -23,8 +23,9 @@ import android.media.session.PlaybackState import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule @@ -33,7 +34,6 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock @@ -63,10 +63,8 @@ class MediaTimeoutListenerTest : SysuiTestCase() { @Mock private lateinit var mediaControllerFactory: MediaControllerFactory @Mock private lateinit var mediaController: MediaController - @Mock private lateinit var executor: DelayableExecutor + private lateinit var executor: FakeExecutor @Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit - @Mock private lateinit var cancellationRunnable: Runnable - @Captor private lateinit var timeoutCaptor: ArgumentCaptor<Runnable> @Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback> @JvmField @Rule val mockito = MockitoJUnit.rule() private lateinit var metadataBuilder: MediaMetadata.Builder @@ -78,7 +76,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { @Before fun setup() { `when`(mediaControllerFactory.create(any())).thenReturn(mediaController) - `when`(executor.executeDelayed(any(), anyLong())).thenReturn(cancellationRunnable) + executor = FakeExecutor(FakeSystemClock()) mediaTimeoutListener = MediaTimeoutListener(mediaControllerFactory, executor) mediaTimeoutListener.timeoutCallback = timeoutCallback @@ -120,7 +118,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { fun testOnMediaDataLoaded_registersTimeout_whenPaused() { mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) verify(mediaController).registerCallback(capture(mediaCallbackCaptor)) - verify(executor).executeDelayed(capture(timeoutCaptor), anyLong()) + assertThat(executor.numPending()).isEqualTo(1) verify(timeoutCallback, never()).invoke(anyString(), anyBoolean()) } @@ -137,6 +135,17 @@ class MediaTimeoutListenerTest : SysuiTestCase() { } @Test + fun testOnMediaDataRemoved_clearsTimeout() { + // GIVEN media that is paused + mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) + assertThat(executor.numPending()).isEqualTo(1) + // WHEN the media is removed + mediaTimeoutListener.onMediaDataRemoved(KEY) + // THEN the timeout runnable is cancelled + assertThat(executor.numPending()).isEqualTo(0) + } + + @Test fun testOnMediaDataLoaded_migratesKeys() { // From not playing mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) @@ -151,7 +160,24 @@ class MediaTimeoutListenerTest : SysuiTestCase() { verify(mediaController).registerCallback(anyObject()) // Enqueues callback - verify(executor).execute(anyObject()) + assertThat(executor.numPending()).isEqualTo(1) + } + + @Test + fun testOnMediaDataLoaded_migratesKeys_noTimeoutExtension() { + // From not playing + mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) + clearInvocations(mediaController) + + // Migrate, still not playing + val playingState = mock(android.media.session.PlaybackState::class.java) + `when`(playingState.state).thenReturn(PlaybackState.STATE_PAUSED) + `when`(mediaController.playbackState).thenReturn(playingState) + mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData) + + // The number of queued timeout tasks remains the same. The timeout task isn't cancelled nor + // is another scheduled + assertThat(executor.numPending()).isEqualTo(1) } @Test @@ -161,7 +187,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder() .setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()) - verify(executor).executeDelayed(capture(timeoutCaptor), anyLong()) + assertThat(executor.numPending()).isEqualTo(1) } @Test @@ -171,7 +197,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder() .setState(PlaybackState.STATE_PLAYING, 0L, 0f).build()) - verify(cancellationRunnable).run() + assertThat(executor.numPending()).isEqualTo(0) } @Test @@ -179,10 +205,9 @@ class MediaTimeoutListenerTest : SysuiTestCase() { // Assuming we have a pending timeout testOnPlaybackStateChanged_schedulesTimeout_whenPaused() - clearInvocations(cancellationRunnable) mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder() .setState(PlaybackState.STATE_STOPPED, 0L, 0f).build()) - verify(cancellationRunnable, never()).run() + assertThat(executor.numPending()).isEqualTo(1) } @Test @@ -190,7 +215,10 @@ class MediaTimeoutListenerTest : SysuiTestCase() { // Assuming we're have a pending timeout testOnPlaybackStateChanged_schedulesTimeout_whenPaused() - timeoutCaptor.value.run() + with(executor) { + advanceClockToNext() + runAllReady() + } verify(timeoutCallback).invoke(eq(KEY), eq(true)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt new file mode 100644 index 000000000000..d26229edf71a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.content.ComponentName +import android.content.Context +import android.media.MediaDescription +import android.media.browse.MediaBrowser +import android.media.session.MediaController +import android.media.session.MediaSession +import android.service.media.MediaBrowserService +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +private const val PACKAGE_NAME = "package" +private const val CLASS_NAME = "class" +private const val TITLE = "song title" +private const val MEDIA_ID = "media ID" +private const val ROOT = "media browser root" + +private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() +private fun <T> eq(value: T): T = Mockito.eq(value) ?: value +private fun <T> any(): T = Mockito.any<T>() + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class ResumeMediaBrowserTest : SysuiTestCase() { + + private lateinit var resumeBrowser: TestableResumeMediaBrowser + private val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + private val description = MediaDescription.Builder() + .setTitle(TITLE) + .setMediaId(MEDIA_ID) + .build() + + @Mock lateinit var callback: ResumeMediaBrowser.Callback + @Mock lateinit var listener: MediaResumeListener + @Mock lateinit var service: MediaBrowserService + @Mock lateinit var browserFactory: MediaBrowserFactory + @Mock lateinit var browser: MediaBrowser + @Mock lateinit var token: MediaSession.Token + @Mock lateinit var mediaController: MediaController + @Mock lateinit var transportControls: MediaController.TransportControls + + @Captor lateinit var connectionCallback: ArgumentCaptor<MediaBrowser.ConnectionCallback> + @Captor lateinit var subscriptionCallback: ArgumentCaptor<MediaBrowser.SubscriptionCallback> + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(browserFactory.create(any(), capture(connectionCallback), any())) + .thenReturn(browser) + + whenever(mediaController.transportControls).thenReturn(transportControls) + + resumeBrowser = TestableResumeMediaBrowser(context, callback, component, browserFactory, + mediaController) + } + + @Test + fun testConnection_connectionFails_callsOnError() { + // When testConnection cannot connect to the service + setupBrowserFailed() + resumeBrowser.testConnection() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testConnection_connects_onConnected() { + // When testConnection can connect to the service + setupBrowserConnection() + resumeBrowser.testConnection() + + // Then it calls onConnected + verify(callback).onConnected() + } + + @Test + fun testConnection_noValidMedia_error() { + // When testConnection can connect to the service, and does not find valid media + setupBrowserConnectionNoResults() + resumeBrowser.testConnection() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testConnection_hasValidMedia_addTrack() { + // When testConnection can connect to the service, and finds valid media + setupBrowserConnectionValidMedia() + resumeBrowser.testConnection() + + // Then it calls addTrack + verify(callback).onConnected() + verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser)) + } + + @Test + fun testFindRecentMedia_connectionFails_error() { + // When findRecentMedia is called and we cannot connect + setupBrowserFailed() + resumeBrowser.findRecentMedia() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testFindRecentMedia_noRoot_error() { + // When findRecentMedia is called and does not get a valid root + setupBrowserConnection() + whenever(browser.getRoot()).thenReturn(null) + resumeBrowser.findRecentMedia() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testFindRecentMedia_connects_onConnected() { + // When findRecentMedia is called and we connect + setupBrowserConnection() + resumeBrowser.findRecentMedia() + + // Then it calls onConnected + verify(callback).onConnected() + } + + @Test + fun testFindRecentMedia_noChildren_error() { + // When findRecentMedia is called and we connect, but do not get any results + setupBrowserConnectionNoResults() + resumeBrowser.findRecentMedia() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testFindRecentMedia_notPlayable_error() { + // When findRecentMedia is called and we connect, but do not get a playable child + setupBrowserConnectionNotPlayable() + resumeBrowser.findRecentMedia() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testFindRecentMedia_hasValidMedia_addTrack() { + // When findRecentMedia is called and we can connect and get playable media + setupBrowserConnectionValidMedia() + resumeBrowser.findRecentMedia() + + // Then it calls addTrack + verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser)) + } + + @Test + fun testRestart_connectionFails_error() { + // When restart is called and we cannot connect + setupBrowserFailed() + resumeBrowser.restart() + + // Then it calls onError + verify(callback).onError() + } + + @Test + fun testRestart_connects() { + // When restart is called and we connect successfully + setupBrowserConnection() + resumeBrowser.restart() + + // Then it creates a new controller and sends play command + verify(transportControls).prepare() + verify(transportControls).play() + + // Then it calls onConnected + verify(callback).onConnected() + } + + /** + * Helper function to mock a failed connection + */ + private fun setupBrowserFailed() { + whenever(browser.connect()).thenAnswer { + connectionCallback.value.onConnectionFailed() + } + } + + /** + * Helper function to mock a successful connection only + */ + private fun setupBrowserConnection() { + whenever(browser.connect()).thenAnswer { + connectionCallback.value.onConnected() + } + whenever(browser.isConnected()).thenReturn(true) + whenever(browser.getRoot()).thenReturn(ROOT) + whenever(browser.sessionToken).thenReturn(token) + } + + /** + * Helper function to mock a successful connection, but no media results + */ + private fun setupBrowserConnectionNoResults() { + setupBrowserConnection() + whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer { + subscriptionCallback.value.onChildrenLoaded(ROOT, emptyList()) + } + } + + /** + * Helper function to mock a successful connection, but no playable results + */ + private fun setupBrowserConnectionNotPlayable() { + setupBrowserConnection() + + val child = MediaBrowser.MediaItem(description, 0) + + whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer { + subscriptionCallback.value.onChildrenLoaded(ROOT, listOf(child)) + } + } + + /** + * Helper function to mock a successful connection with playable media + */ + private fun setupBrowserConnectionValidMedia() { + setupBrowserConnection() + + val child = MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_PLAYABLE) + + whenever(browser.serviceComponent).thenReturn(component) + whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer { + subscriptionCallback.value.onChildrenLoaded(ROOT, listOf(child)) + } + } + + /** + * Override so media controller use is testable + */ + private class TestableResumeMediaBrowser( + context: Context, + callback: Callback, + componentName: ComponentName, + browserFactory: MediaBrowserFactory, + private val fakeController: MediaController + ) : ResumeMediaBrowser(context, callback, componentName, browserFactory) { + + override fun createMediaController(token: MediaSession.Token): MediaController { + return fakeController + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java new file mode 100644 index 000000000000..a5dead0f3258 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.customize; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.Bundle; +import android.testing.AndroidTestingRunner; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TileAdapterDelegateTest extends SysuiTestCase { + + private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position; + private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position; + private static final int POSITION_STRING_ID = R.string.accessibility_qs_edit_position; + + @Mock + private TileAdapter.Holder mHolder; + + private AccessibilityNodeInfoCompat mInfo; + private TileAdapterDelegate mDelegate; + private View mView; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mView = new View(mContext); + mDelegate = new TileAdapterDelegate(); + mInfo = AccessibilityNodeInfoCompat.obtain(); + } + + @Test + public void testInfoNoSpecialActionsWhenNoHolder() { + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) { + if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID + || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) { + fail("It should not have special action " + action.getId()); + } + } + } + + @Test + public void testInfoNoSpecialActionsWhenCannotStartAccessibleAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(false); + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) { + if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID + || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) { + fail("It should not have special action " + action.getId()); + } + } + } + + @Test + public void testNoCollectionItemInfo() { + mInfo.setCollectionItemInfo( + AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, false)); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(mInfo.getCollectionItemInfo()).isNull(); + } + + @Test + public void testStateDescriptionHasPositionForCurrentTile() { + mView.setTag(mHolder); + int position = 3; + when(mHolder.getLayoutPosition()).thenReturn(position); + when(mHolder.isCurrentTile()).thenReturn(true); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + String expectedString = mContext.getString(POSITION_STRING_ID, position); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(mInfo.getStateDescription()).isEqualTo(expectedString); + } + + @Test + public void testStateDescriptionEmptyForNotCurrentTile() { + mView.setTag(mHolder); + int position = 3; + when(mHolder.getLayoutPosition()).thenReturn(position); + when(mHolder.isCurrentTile()).thenReturn(false); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(mInfo.getStateDescription()).isEqualTo(""); + } + + @Test + public void testClickAddAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(true); + when(mHolder.canRemove()).thenReturn(false); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + + String expectedString = mContext.getString(R.string.accessibility_qs_edit_tile_add_action); + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK); + assertThat(action.getLabel().toString()).contains(expectedString); + } + + @Test + public void testClickRemoveAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(false); + when(mHolder.canRemove()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + + String expectedString = mContext.getString( + R.string.accessibility_qs_edit_remove_tile_action); + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK); + assertThat(action.getLabel().toString()).contains(expectedString); + } + + @Test + public void testNoClickAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(false); + when(mHolder.canRemove()).thenReturn(false); + mInfo.addAction(AccessibilityNodeInfo.ACTION_CLICK); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK); + assertThat(action).isNull(); + } + + @Test + public void testAddToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNotNull(); + } + + @Test + public void testNoAddToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(false); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNull(); + } + + @Test + public void testMoveToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.isCurrentTile()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNotNull(); + } + + @Test + public void testNoMoveToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.isCurrentTile()).thenReturn(false); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNull(); + } + + @Test + public void testNoInteractionsWhenCannotTakeAccessibleAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(false); + + mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null); + mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, new Bundle()); + mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, new Bundle()); + + verify(mHolder, never()).toggleState(); + verify(mHolder, never()).startAccessibleAdd(); + verify(mHolder, never()).startAccessibleMove(); + } + + @Test + public void testClickActionTogglesState() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null); + + verify(mHolder).toggleState(); + } + + @Test + public void testAddToPositionActionStartsAccessibleAdd() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, null); + + verify(mHolder).startAccessibleAdd(); + } + + @Test + public void testMoveToPositionActionStartsAccessibleMove() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, null); + + verify(mHolder).startAccessibleMove(); + } + + private AccessibilityNodeInfoCompat.AccessibilityActionCompat getActionForId( + AccessibilityNodeInfoCompat info, int action) { + for (AccessibilityNodeInfoCompat.AccessibilityActionCompat a : info.getActionList()) { + if (a.getId() == action) { + return a; + } + } + return null; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index e98b6b69ee76..4c9e141c45cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -32,7 +32,9 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import org.junit.Before; import org.junit.Test; @@ -61,6 +63,12 @@ public class RecordingServiceTest extends SysuiTestCase { private Executor mExecutor; @Mock private CurrentUserContextTracker mUserContextTracker; + private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() { + public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, + boolean requiresShadeOpen) { + action.onDismiss(); + } + }; private RecordingService mRecordingService; @@ -68,7 +76,7 @@ public class RecordingServiceTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger, - mNotificationManager, mUserContextTracker)); + mNotificationManager, mUserContextTracker, mKeyguardDismissUtil)); // Return actual context info doReturn(mContext).when(mRecordingService).getApplicationContext(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index 402a99d4c23d..dee6020dfd56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -108,11 +108,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { return new TestableAlertingNotificationManager(); } - protected StatusBarNotification createNewNotification(int id) { - Notification.Builder n = new Notification.Builder(mContext, "") - .setSmallIcon(R.drawable.ic_person) - .setContentTitle("Title") - .setContentText("Text"); + protected StatusBarNotification createNewSbn(int id, Notification.Builder n) { return new StatusBarNotification( TEST_PACKAGE_NAME /* pkg */, TEST_PACKAGE_NAME, @@ -126,6 +122,14 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { 0 /* postTime */); } + protected StatusBarNotification createNewNotification(int id) { + Notification.Builder n = new Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text"); + return createNewSbn(id, n); + } + @Before public void setUp() { mTestHandler = Handler.createAsync(Looper.myLooper()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 80fa8cc7d931..92a2c8738344 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -211,6 +211,19 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { } @Test + public void testUpdateNotificationViews_appOps() throws Exception { + NotificationEntry entry0 = createEntry(); + entry0.setRow(spy(entry0.getRow())); + when(mEntryManager.getVisibleNotifications()).thenReturn( + Lists.newArrayList(entry0)); + mListContainer.addContainerView(entry0.getRow()); + + mViewHierarchyManager.updateNotificationViews(); + + verify(entry0.getRow(), times(1)).showAppOpsIcons(any()); + } + + @Test public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() { // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews() mMadeReentrantCall = false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index ae39035e8666..314b19140e7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -72,6 +72,8 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { private Notification mNotification; private AppOpsCoordinator mAppOpsCoordinator; private NotifFilter mForegroundFilter; + private NotifCollectionListener mNotifCollectionListener; + private AppOpsController.Callback mAppOpsCallback; private NotifLifetimeExtender mForegroundNotifLifetimeExtender; private FakeSystemClock mClock = new FakeSystemClock(); @@ -108,6 +110,18 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { lifetimeExtenderCaptor.capture()); mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue(); + // capture notifCollectionListener + ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor = + ArgumentCaptor.forClass(NotifCollectionListener.class); + verify(mNotifPipeline, times(1)).addCollectionListener( + notifCollectionCaptor.capture()); + mNotifCollectionListener = notifCollectionCaptor.getValue(); + + // capture app ops callback + ArgumentCaptor<AppOpsController.Callback> appOpsCaptor = + ArgumentCaptor.forClass(AppOpsController.Callback.class); + verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture()); + mAppOpsCallback = appOpsCaptor.getValue(); } @Test @@ -201,4 +215,134 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { assertFalse(mForegroundNotifLifetimeExtender .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK)); } + + @Test + public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() { + // GIVEN three current notifications, two with the same key but from different users + NotificationEntry entry1 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(1) + .build(); + NotificationEntry entry2 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + NotificationEntry entry3_diffUser = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID + 1)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser)); + + // GIVEN that only entry2 has a standard layout + when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG)) + .thenReturn(new ArraySet<>(List.of(entry2.getKey()))); + + // WHEN a new app ops code comes in + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); + mExecutor.runAllReady(); + + // THEN entry2's app ops are updated, but no one else's are + assertEquals( + new ArraySet<>(), + entry1.mActiveAppOps); + assertEquals( + new ArraySet<>(List.of(47)), + entry2.mActiveAppOps); + assertEquals( + new ArraySet<>(), + entry3_diffUser.mActiveAppOps); + } + + @Test + public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() { + // GIVEN three notifications with standard layouts + NotificationEntry entry1 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(1) + .build(); + NotificationEntry entry2 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + NotificationEntry entry3 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(3) + .build(); + when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3)); + when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG)) + .thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(), + entry3.getKey()))); + + // WHEN a new app ops code comes in + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); + mExecutor.runAllReady(); + + // THEN all entries get updated + assertEquals( + new ArraySet<>(List.of(47)), + entry1.mActiveAppOps); + assertEquals( + new ArraySet<>(List.of(47)), + entry2.mActiveAppOps); + assertEquals( + new ArraySet<>(List.of(47)), + entry3.mActiveAppOps); + } + + @Test + public void testAppOpsAreRemoved() { + // GIVEN One notification which is associated with app ops + NotificationEntry entry = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry)); + when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG)) + .thenReturn(new ArraySet<>(List.of(entry.getKey()))); + + // GIVEN that the notification's app ops are already [47, 33] + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); + mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true); + mExecutor.runAllReady(); + assertEquals( + new ArraySet<>(List.of(47, 33)), + entry.mActiveAppOps); + + // WHEN one of the app ops is removed + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false); + mExecutor.runAllReady(); + + // THEN the entry's active app ops are updated as well + assertEquals( + new ArraySet<>(List.of(33)), + entry.mActiveAppOps); + } + + @Test + public void testNullAppOps() { + // GIVEN one notification with app ops + NotificationEntry entry = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + entry.mActiveAppOps.clear(); + entry.mActiveAppOps.addAll(List.of(47, 33)); + + // WHEN the notification is updated and the foreground service controller returns null for + // this notification + when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(), + entry.getSbn().getPackageName())).thenReturn(null); + mNotifCollectionListener.onEntryUpdated(entry); + + // THEN the entry's active app ops is updated to empty + assertTrue(entry.mActiveAppOps.isEmpty()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java new file mode 100644 index 000000000000..43d8b50bcf72 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 static android.app.AppOpsManager.OP_CAMERA; +import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.UiThreadTest; +import android.util.ArraySet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@UiThreadTest +public class AppOpsInfoTest extends SysuiTestCase { + private static final String TEST_PACKAGE_NAME = "test_package"; + private static final int TEST_UID = 1; + + private AppOpsInfo mAppOpsInfo; + private final PackageManager mMockPackageManager = mock(PackageManager.class); + private final NotificationGuts mGutsParent = mock(NotificationGuts.class); + private StatusBarNotification mSbn; + private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); + + @Before + public void setUp() throws Exception { + // Inflate the layout + final LayoutInflater layoutInflater = LayoutInflater.from(mContext); + mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null); + mAppOpsInfo.setGutsParent(mGutsParent); + + // PackageManager must return a packageInfo and applicationInfo. + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt())) + .thenReturn(packageInfo); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = TEST_UID; // non-zero + when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn( + applicationInfo); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + new Notification(), UserHandle.CURRENT, null, 0); + } + + @Test + public void testBindNotification_SetsTextApplicationName() { + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); + final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname); + assertTrue(textView.getText().toString().contains("App Name")); + } + + @Test + public void testBindNotification_SetsPackageIcon() { + final Drawable iconDrawable = mock(Drawable.class); + when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) + .thenReturn(iconDrawable); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); + final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon); + assertEquals(iconDrawable, iconView.getDrawable()); + } + + @Test + public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + final CountDownLatch latch = new CountDownLatch(1); + mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid, + ArraySet<Integer> ops) -> { + assertEquals(TEST_PACKAGE_NAME, pkg); + assertEquals(expectedOps, ops); + assertEquals(TEST_UID, uid); + latch.countDown(); + }, mSbn, mUiEventLogger, expectedOps); + + final View settingsButton = mAppOpsInfo.findViewById(R.id.settings); + settingsButton.performClick(); + // Verify that listener was triggered. + assertEquals(0, latch.getCount()); + } + + @Test + public void testBindNotification_LogsOpen() throws Exception { + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); + assertEquals(1, mUiEventLogger.numLogs()); + assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(), + mUiEventLogger.eventId(0)); + } + + @Test + public void testOk() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + final CountDownLatch latch = new CountDownLatch(1); + mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid, + ArraySet<Integer> ops) -> { + assertEquals(TEST_PACKAGE_NAME, pkg); + assertEquals(expectedOps, ops); + assertEquals(TEST_UID, uid); + latch.countDown(); + }, mSbn, mUiEventLogger, expectedOps); + + final View okButton = mAppOpsInfo.findViewById(R.id.ok); + okButton.performClick(); + assertEquals(1, latch.getCount()); + verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean()); + } + + @Test + public void testPrompt_camera() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is using the camera.", prompt.getText()); + } + + @Test + public void testPrompt_mic() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_RECORD_AUDIO); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is using the microphone.", prompt.getText()); + } + + @Test + public void testPrompt_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen.", prompt.getText()); + } + + @Test + public void testPrompt_camera_mic() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + expectedOps.add(OP_RECORD_AUDIO); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is using the microphone and camera.", prompt.getText()); + } + + @Test + public void testPrompt_camera_mic_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + expectedOps.add(OP_RECORD_AUDIO); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen and using" + + " the microphone and camera.", prompt.getText()); + } + + @Test + public void testPrompt_camera_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen and using" + + " the camera.", prompt.getText()); + } + + @Test + public void testPrompt_mic_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_RECORD_AUDIO); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen and using" + + " the microphone.", prompt.getText()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 4758d2318889..2684cc29aa93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -35,10 +35,12 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AppOpsManager; import android.app.NotificationChannel; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.util.ArraySet; import android.view.View; import androidx.test.filters.SmallTest; @@ -211,6 +213,46 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test + public void testShowAppOps_noHeader() { + // public notification is custom layout - no header + mGroupRow.setSensitive(true, true); + mGroupRow.setAppOpsOnClickListener(null); + mGroupRow.showAppOpsIcons(null); + } + + @Test + public void testShowAppOpsIcons_header() { + NotificationContentView publicLayout = mock(NotificationContentView.class); + mGroupRow.setPublicLayout(publicLayout); + NotificationContentView privateLayout = mock(NotificationContentView.class); + mGroupRow.setPrivateLayout(privateLayout); + NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); + when(mockContainer.getNotificationChildCount()).thenReturn(1); + mGroupRow.setChildrenContainer(mockContainer); + + ArraySet<Integer> ops = new ArraySet<>(); + ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS); + mGroupRow.showAppOpsIcons(ops); + + verify(mockContainer, times(1)).showAppOpsIcons(ops); + verify(privateLayout, times(1)).showAppOpsIcons(ops); + verify(publicLayout, times(1)).showAppOpsIcons(ops); + + } + + @Test + public void testAppOpsOnClick() { + ExpandableNotificationRow.OnAppOpsClickListener l = mock( + ExpandableNotificationRow.OnAppOpsClickListener.class); + View view = mock(View.class); + + mGroupRow.setAppOpsOnClickListener(l); + + mGroupRow.getAppOpsOnClickListener().onClick(view); + verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any()); + } + + @Test public void testHeadsUpAnimatingAwayListener() { mGroupRow.setHeadsUpAnimatingAway(true); Assert.assertEquals(true, mHeadsUpAnimatingAway); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index b02f2746ce7a..ed4f8b330e23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -76,6 +76,32 @@ public class NotificationContentViewTest extends SysuiTestCase { @Test @UiThreadTest + public void testShowAppOpsIcons() { + View mockContracted = mock(NotificationHeaderView.class); + when(mockContracted.findViewById(com.android.internal.R.id.mic)) + .thenReturn(mockContracted); + View mockExpanded = mock(NotificationHeaderView.class); + when(mockExpanded.findViewById(com.android.internal.R.id.mic)) + .thenReturn(mockExpanded); + View mockHeadsUp = mock(NotificationHeaderView.class); + when(mockHeadsUp.findViewById(com.android.internal.R.id.mic)) + .thenReturn(mockHeadsUp); + + mView.setContractedChild(mockContracted); + mView.setExpandedChild(mockExpanded); + mView.setHeadsUpChild(mockHeadsUp); + + ArraySet<Integer> ops = new ArraySet<>(); + ops.add(AppOpsManager.OP_RECORD_AUDIO); + mView.showAppOpsIcons(ops); + + verify(mockContracted, times(1)).setVisibility(View.VISIBLE); + verify(mockExpanded, times(1)).setVisibility(View.VISIBLE); + verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE); + } + + @Test + @UiThreadTest public void testExpandButtonFocusIsCalled() { View mockContractedEB = mock(NotificationExpandButton.class); View mockContracted = mock(NotificationHeaderView.class); 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 d2ff2ad8a684..0c6409b38d21 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 @@ -421,6 +421,7 @@ public class NotificationTestHelper { mBindStage, mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), + mock(ExpandableNotificationRow.OnAppOpsClickListener.class), mock(FalsingManager.class), mStatusBarStateController, mPeopleNotificationIdentifier); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index fc7d0ce30687..0e4b053833b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -16,12 +16,15 @@ package com.android.systemui.statusbar.policy; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; +import android.app.Notification; import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -30,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import org.junit.Before; import org.junit.Test; @@ -84,5 +88,25 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { assertTrue("Heads up should live long enough", mLivesPastNormalTime); assertFalse(mHeadsUpManager.isAlerting(mEntry.getKey())); } + + @Test + public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { + HeadsUpManager.HeadsUpEntry ongoingCall = mHeadsUpManager.new HeadsUpEntry(); + ongoingCall.setEntry(new NotificationEntryBuilder() + .setSbn(createNewSbn(0, + new Notification.Builder(mContext, "") + .setCategory(Notification.CATEGORY_CALL) + .setOngoing(true))) + .build()); + + HeadsUpManager.HeadsUpEntry activeRemoteInput = mHeadsUpManager.new HeadsUpEntry(); + activeRemoteInput.setEntry(new NotificationEntryBuilder() + .setSbn(createNewNotification(1)) + .build()); + activeRemoteInput.remoteInputActive = true; + + assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0); + assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 6fffcff41a4f..56598f7a5bfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -3,6 +3,8 @@ package com.android.systemui.statusbar.policy; import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -10,6 +12,7 @@ import static org.mockito.Mockito.when; import android.net.NetworkCapabilities; import android.os.Looper; import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -259,6 +262,25 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { assertDataNetworkNameEquals(newDataName); } + @Test + public void testIsDataInService_true() { + setupDefaultSignal(); + assertTrue(mNetworkController.isMobileDataNetworkInService()); + } + + @Test + public void testIsDataInService_noSignal_false() { + assertFalse(mNetworkController.isMobileDataNetworkInService()); + } + + @Test + public void testIsDataInService_notInService_false() { + setupDefaultSignal(); + setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE); + setDataRegState(ServiceState.STATE_OUT_OF_SERVICE); + assertFalse(mNetworkController.isMobileDataNetworkInService()); + } + private void testDataActivity(int direction, boolean in, boolean out) { updateDataActivity(direction); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java index d5ba381bfcac..e7acfae24f30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java @@ -95,6 +95,11 @@ public class FakeNetworkController extends BaseLeakChecker<SignalCallback> } @Override + public boolean isMobileDataNetworkInService() { + return false; + } + + @Override public int getNumberSubscriptions() { return 0; } diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java index fbe8c04bd59c..38983488654e 100644 --- a/services/core/java/android/os/UserManagerInternal.java +++ b/services/core/java/android/os/UserManagerInternal.java @@ -227,6 +227,14 @@ public abstract class UserManagerInternal { public abstract @NonNull List<UserInfo> getUsers(boolean excludeDying); /** + * Internal implementation of getUsers does not check permissions. + * This improves performance for calls from inside system server which already have permissions + * checked. + */ + public abstract @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + boolean excludePreCreated); + + /** * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group * and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled. * diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 4fab067d1d3b..651f941be0b1 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3109,6 +3109,14 @@ class AlarmManagerService extends SystemService { mPendingBackgroundAlarms.removeAt(i); } } + for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) { + final Alarm a = mPendingNonWakeupAlarms.get(i); + if (a.matches(operation, directReceiver)) { + // Don't set didRemove, since this doesn't impact the scheduled alarms. + mPendingNonWakeupAlarms.remove(i); + decrementAlarmCount(a.uid, 1); + } + } if (didRemove) { if (DEBUG_BATCH) { Slog.v(TAG, "remove(operation) changed bounds; rebatching"); diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 829fca66ec0d..9fc8f0b5a3c3 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -454,10 +454,14 @@ public class RescueParty { public boolean mayObservePackage(String packageName) { PackageManager pm = mContext.getPackageManager(); try { - // A package is a Mainline module if this is non-null + // A package is a module if this is non-null if (pm.getModuleInfo(packageName, 0) != null) { return true; } + } catch (PackageManager.NameNotFoundException ignore) { + } + + try { ApplicationInfo info = pm.getApplicationInfo(packageName, 0); return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; } catch (PackageManager.NameNotFoundException e) { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 678387c540ed..391923324d0c 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -4745,15 +4745,20 @@ class StorageManagerService extends IStorageManager.Stub } } - public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) { + public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode, + int previousMode) { final long token = Binder.clearCallingIdentity(); try { if (mIsFuseEnabled) { // When using FUSE, we may need to kill the app if the op changes switch(code) { case OP_REQUEST_INSTALL_PACKAGES: - // Always kill regardless of op change, to remount apps /storage - killAppForOpChange(code, uid); + if (previousMode == MODE_ALLOWED || mode == MODE_ALLOWED) { + // If we transition to/from MODE_ALLOWED, kill the app to make + // sure it has the correct view of /storage. Changing between + // MODE_DEFAULT / MODE_ERRORED is a no-op + killAppForOpChange(code, uid); + } return; case OP_MANAGE_EXTERNAL_STORAGE: if (mode != MODE_ALLOWED) { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index be080e5cce62..daae1a11059c 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -134,6 +134,7 @@ final class UiModeManagerService extends SystemService { int mCurUiMode = 0; private int mSetUiMode = 0; private boolean mHoldingConfiguration = false; + private int mCurrentUser; private Configuration mConfiguration = new Configuration(); boolean mSystemReady; @@ -323,6 +324,7 @@ final class UiModeManagerService extends SystemService { @Override public void onSwitchUser(int userHandle) { super.onSwitchUser(userHandle); + mCurrentUser = userHandle; getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); verifySetupWizardCompleted(); } @@ -485,6 +487,9 @@ final class UiModeManagerService extends SystemService { * @return True if the new value is different from the old value. False otherwise. */ private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) { + if (mCarModeEnabled || mCar) { + return false; + } int oldNightMode = mNightMode; if (mSetupWizardComplete) { mNightMode = Secure.getIntForUser(context.getContentResolver(), @@ -725,16 +730,30 @@ final class UiModeManagerService extends SystemService { @Override public boolean setNightModeActivated(boolean active) { + if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + != PackageManager.PERMISSION_GRANTED)) { + Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); + return false; + } + final int user = Binder.getCallingUserHandle().getIdentifier(); + if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS) + != PackageManager.PERMISSION_GRANTED) { + Slog.e(TAG, "Target user is not current user," + + " INTERACT_ACROSS_USERS permission is required"); + return false; + + } synchronized (mLock) { - final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { unregisterScreenOffEventLocked(); mOverrideNightModeOff = !active; mOverrideNightModeOn = active; - mOverrideNightModeUser = user; - persistNightModeOverrides(user); + mOverrideNightModeUser = mCurrentUser; + persistNightModeOverrides(mCurrentUser); } else if (mNightMode == UiModeManager.MODE_NIGHT_NO && active) { mNightMode = UiModeManager.MODE_NIGHT_YES; @@ -744,7 +763,7 @@ final class UiModeManagerService extends SystemService { } updateConfigurationLocked(); applyConfigurationExternallyLocked(); - persistNightMode(user); + persistNightMode(mCurrentUser); return true; } finally { Binder.restoreCallingIdentity(ident); @@ -1015,7 +1034,7 @@ final class UiModeManagerService extends SystemService { private void persistNightMode(int user) { // Only persist setting if not in car mode - if (mCarModeEnabled) return; + if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mNightMode, user); Secure.putLongForUser(getContext().getContentResolver(), @@ -1028,7 +1047,7 @@ final class UiModeManagerService extends SystemService { private void persistNightModeOverrides(int user) { // Only persist setting if not in car mode - if (mCarModeEnabled) return; + if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user); Secure.putIntForUser(getContext().getContentResolver(), @@ -1079,7 +1098,7 @@ final class UiModeManagerService extends SystemService { } // Override night mode in power save mode if not in car mode - if (mPowerSave && !mCarModeEnabled) { + if (mPowerSave && !mCarModeEnabled && !mCar) { uiMode &= ~Configuration.UI_MODE_NIGHT_NO; uiMode |= Configuration.UI_MODE_NIGHT_YES; } else { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index e1bb4cda8dc2..dd0e1f6458f9 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -453,16 +453,16 @@ public final class ActiveServices { } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification, - String callingPackage, @Nullable String callingFeatureId, final int userId) + int callingPid, int callingUid, boolean fgRequired, String callingPackage, + @Nullable String callingFeatureId, final int userId) throws TransactionTooLargeException { return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired, - hideFgNotification, callingPackage, callingFeatureId, userId, false); + callingPackage, callingFeatureId, userId, false); } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification, - String callingPackage, @Nullable String callingFeatureId, final int userId, + int callingPid, int callingUid, boolean fgRequired, String callingPackage, + @Nullable String callingFeatureId, final int userId, boolean allowBackgroundActivityStarts) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); @@ -609,7 +609,6 @@ public final class ActiveServices { r.startRequested = true; r.delayedStop = false; r.fgRequired = fgRequired; - r.hideFgNotification = hideFgNotification; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants, callingUid)); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 491579a5a294..3b8518004309 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1438,10 +1438,6 @@ public class ActivityManagerService extends IActivityManager.Stub final Injector mInjector; - /** The package verifier app. */ - private String mPackageVerifier; - private int mPackageVerifierUid = UserHandle.USER_NULL; - static final class ProcessChangeItem { static final int CHANGE_ACTIVITIES = 1<<0; static final int CHANGE_FOREGROUND_SERVICES = 1<<1; @@ -2350,18 +2346,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (phase == PHASE_SYSTEM_SERVICES_READY) { mService.mBatteryStatsService.systemServicesReady(); mService.mServices.systemServicesReady(); - mService.mPackageVerifier = ArrayUtils.firstOrNull( - LocalServices.getService(PackageManagerInternal.class).getKnownPackageNames( - PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM)); - if (mService.mPackageVerifier != null) { - try { - mService.mPackageVerifierUid = - getContext().getPackageManager().getPackageUid( - mService.mPackageVerifier, UserHandle.USER_SYSTEM); - } catch (NameNotFoundException e) { - Slog.wtf(TAG, "Package manager couldn't get package verifier uid", e); - } - } } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { mService.startBroadcastObservers(); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { @@ -10530,7 +10514,7 @@ public class ActivityManagerService extends IActivityManager.Stub private void dumpEverything(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage, boolean dumpClient, boolean dumpNormalPriority, - int dumpAppId) { + int dumpAppId, boolean dumpProxies) { ActiveServices.ServiceDumper sdumper; @@ -10589,7 +10573,7 @@ public class ActivityManagerService extends IActivityManager.Stub } sdumper.dumpWithClient(); } - if (dumpPackage == null) { + if (dumpPackage == null && dumpProxies) { // Intentionally dropping the lock for this, because dumpBinderProxies() will make many // outgoing binder calls to retrieve interface descriptors; while that is system code, // there is nothing preventing an app from overriding this implementation by talking to @@ -10998,13 +10982,14 @@ public class ActivityManagerService extends IActivityManager.Stub // dumpEverything() will take the lock when needed, and momentarily drop // it for dumping client state. dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient, - dumpNormalPriority, dumpAppId); + dumpNormalPriority, dumpAppId, true /* dumpProxies */); } else { // Take the lock here, so we get a consistent state for the entire dump; - // dumpEverything() will take the lock as well, but that is fine. + // dumpEverything() will take the lock as well, which is fine for everything + // except dumping proxies, which can take a long time; exclude them. synchronized(this) { dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient, - dumpNormalPriority, dumpAppId); + dumpNormalPriority, dumpAppId, false /* dumpProxies */); } } } @@ -15007,8 +14992,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ComponentName startService(IApplicationThread caller, Intent service, - String resolvedType, boolean requireForeground, boolean hideForegroundNotification, - String callingPackage, String callingFeatureId, int userId) + String resolvedType, boolean requireForeground, String callingPackage, + String callingFeatureId, int userId) throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); // Refuse possible leaked file descriptors @@ -15020,27 +15005,17 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("callingPackage cannot be null"); } - final int callingUid = Binder.getCallingUid(); - if (requireForeground && hideForegroundNotification) { - if (!UserHandle.isSameApp(callingUid, mPackageVerifierUid) - || !callingPackage.equals(mPackageVerifier)) { - throw new IllegalArgumentException( - "Only the package verifier can hide its foreground service notification"); - } - Slog.i(TAG, "Foreground service notification hiding requested by " + callingPackage); - } - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground); synchronized(this) { final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); ComponentName res; try { res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, - requireForeground, hideForegroundNotification, - callingPackage, callingFeatureId, userId); + requireForeground, callingPackage, callingFeatureId, userId); } finally { Binder.restoreCallingIdentity(origId); } @@ -19480,7 +19455,7 @@ public class ActivityManagerService extends IActivityManager.Stub ComponentName res; try { res = mServices.startServiceLocked(null, service, - resolvedType, -1, uid, fgRequired, false, callingPackage, + resolvedType, -1, uid, fgRequired, callingPackage, callingFeatureId, userId, allowBackgroundActivityStarts); } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index a512cca7bac4..149e3baa90e7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -654,7 +654,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println("Starting service: " + intent); pw.flush(); ComponentName cn = mInterface.startService(null, intent, intent.getType(), - asForeground, false, SHELL_PACKAGE_NAME, null, mUserId); + asForeground, SHELL_PACKAGE_NAME, null, mUserId); if (cn == null) { err.println("Error: Not found; no service started."); return -1; diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index c5047e5eed03..8112bb854b71 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import android.app.ActivityManager; import android.app.ActivityThread; +import android.app.ApplicationExitInfo; import android.os.Debug; import android.os.Handler; import android.os.Message; @@ -132,11 +133,15 @@ public final class CachedAppOptimizer { static final int REPORT_UNFREEZE_MSG = 4; //TODO:change this static definition into a configurable flag. - static final int FREEZE_TIMEOUT_MS = 500; + static final int FREEZE_TIMEOUT_MS = 10000; static final int DO_FREEZE = 1; static final int REPORT_UNFREEZE = 2; + // Bitfield values for sync/async transactions reveived by frozen processes + static final int SYNC_RECEIVED_WHILE_FROZEN = 1; + static final int ASYNC_RECEIVED_WHILE_FROZEN = 2; + /** * This thread must be moved to the system background cpuset. * If that doesn't happen, it's probably going to draw a lot of power. @@ -479,6 +484,30 @@ public final class CachedAppOptimizer { private static native void enableFreezerInternal(boolean enable); /** + * Informs binder that a process is about to be frozen. If freezer is enabled on a process via + * this method, this method will synchronously dispatch all pending transactions to the + * specified pid. This method will not add significant latencies when unfreezing. + * After freezing binder calls, binder will block all transaction to the frozen pid, and return + * an error to the sending process. + * + * @param pid the target pid for which binder transactions are to be frozen + * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze + * binder for the specificed pid. + * + * @throws RuntimeException in case a flush/freeze operation could not complete successfully. + */ + private static native void freezeBinder(int pid, boolean freeze); + + /** + * Retrieves binder freeze info about a process. + * @param pid the pid for which binder freeze info is to be retrieved. + * + * @throws RuntimeException if the operation could not complete successfully. + * @return a bit field reporting the binder freeze info for the process. + */ + private static native int getBinderFreezeInfo(int pid); + + /** * Determines whether the freezer is supported by this system */ public static boolean isFreezerSupported() { @@ -714,6 +743,37 @@ public final class CachedAppOptimizer { return; } + boolean processKilled = false; + + try { + int freezeInfo = getBinderFreezeInfo(app.pid); + + if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) { + Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " " + + " received sync transactions while frozen, killing"); + app.kill("Sync transaction while in frozen state", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + processKilled = true; + } + + if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) { + Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " " + + " received async transactions while frozen"); + } + } catch (Exception e) { + Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " " + + app.processName + ". Killing it. Exception: " + e); + app.kill("Unable to query binder frozen stats", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + processKilled = true; + } + + if (processKilled) { + return; + } + long freezeTime = app.freezeUnfreezeTime; try { @@ -727,6 +787,17 @@ public final class CachedAppOptimizer { } if (!app.frozen) { + try { + freezeBinder(app.pid, false); + } catch (RuntimeException e) { + Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName + + ". Killing it"); + app.kill("Unable to unfreeze", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + return; + } + if (DEBUG_FREEZER) { Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName); } @@ -1039,6 +1110,14 @@ public final class CachedAppOptimizer { return; } + try { + freezeBinder(pid, true); + } catch (RuntimeException e) { + // TODO: it might be preferable to kill the target pid in this case + Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); + return; + } + if (pid == 0 || proc.frozen) { // Already frozen or not a real process, either one being // launched or one being killed diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java index b36e3c7b9e35..50d58f02baa7 100644 --- a/services/core/java/com/android/server/am/PendingTempWhitelists.java +++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java @@ -32,13 +32,13 @@ final class PendingTempWhitelists { void put(int uid, ActivityManagerService.PendingTempWhitelist value) { mPendingTempWhitelist.put(uid, value); - mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag); + mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag); } void removeAt(int index) { final int uid = mPendingTempWhitelist.keyAt(index); mPendingTempWhitelist.removeAt(index); - mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid); + mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid); } ActivityManagerService.PendingTempWhitelist get(int uid) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 19d5a3125f3a..1b65dbac2294 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -104,7 +104,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? boolean delayed; // are we waiting to start this service in the background? boolean fgRequired; // is the service required to go foreground after starting? - boolean hideFgNotification; // Hide the fg service notification boolean fgWaiting; // is a timeout for going foreground already scheduled? boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. @@ -824,9 +823,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } public void postNotification() { - if (hideFgNotification) { - return; - } final int appUid = appInfo.uid; final int appPid = app.pid; if (foregroundId != 0 && foregroundNoti != null) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c5bdb9edd069..6cecb6aedd45 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -39,6 +39,7 @@ import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; import static android.app.AppOpsManager.OpEventProxyInfo; import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING; @@ -2198,6 +2199,7 @@ public class AppOpsService extends IAppOpsService.Stub { updatePermissionRevokedCompat(uid, code, mode); } + int previousMode; synchronized (this) { final int defaultMode = AppOpsManager.opToDefaultMode(code); @@ -2206,12 +2208,14 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == defaultMode) { return; } + previousMode = AppOpsManager.MODE_DEFAULT; uidState = new UidState(uid); uidState.opModes = new SparseIntArray(); uidState.opModes.put(code, mode); mUidStates.put(uid, uidState); scheduleWriteLocked(); } else if (uidState.opModes == null) { + previousMode = AppOpsManager.MODE_DEFAULT; if (mode != defaultMode) { uidState.opModes = new SparseIntArray(); uidState.opModes.put(code, mode); @@ -2221,6 +2225,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) { return; } + previousMode = uidState.opModes.get(code); if (mode == defaultMode) { uidState.opModes.delete(code); if (uidState.opModes.size() <= 0) { @@ -2235,7 +2240,7 @@ public class AppOpsService extends IAppOpsService.Stub { } notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback); - notifyOpChangedSync(code, uid, null, mode); + notifyOpChangedSync(code, uid, null, mode, previousMode); } /** @@ -2414,11 +2419,12 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) { + private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode, + int previousMode) { final StorageManagerInternal storageManagerInternal = LocalServices.getService(StorageManagerInternal.class); if (storageManagerInternal != null) { - storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode); + storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode, previousMode); } } @@ -2450,11 +2456,13 @@ public class AppOpsService extends IAppOpsService.Stub { return; } + int previousMode = AppOpsManager.MODE_DEFAULT; synchronized (this) { UidState uidState = getUidStateLocked(uid, false); Op op = getOpLocked(code, uid, packageName, null, bypass, true); if (op != null) { if (op.mode != mode) { + previousMode = op.mode; op.mode = mode; if (uidState != null) { uidState.evalForegroundOps(mOpModeWatchers); @@ -2491,7 +2499,7 @@ public class AppOpsService extends IAppOpsService.Stub { this, repCbs, code, uid, packageName)); } - notifyOpChangedSync(code, uid, packageName, mode); + notifyOpChangedSync(code, uid, packageName, mode, previousMode); } private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code, @@ -2534,7 +2542,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private static ArrayList<ChangeRec> addChange(ArrayList<ChangeRec> reports, - int op, int uid, String packageName) { + int op, int uid, String packageName, int previousMode) { boolean duplicate = false; if (reports == null) { reports = new ArrayList<>(); @@ -2549,7 +2557,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (!duplicate) { - reports.add(new ChangeRec(op, uid, packageName)); + reports.add(new ChangeRec(op, uid, packageName, previousMode)); } return reports; @@ -2557,7 +2565,7 @@ public class AppOpsService extends IAppOpsService.Stub { private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks( HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks, - int op, int uid, String packageName, ArraySet<ModeCallback> cbs) { + int op, int uid, String packageName, int previousMode, ArraySet<ModeCallback> cbs) { if (cbs == null) { return callbacks; } @@ -2568,7 +2576,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i=0; i<N; i++) { ModeCallback cb = cbs.valueAt(i); ArrayList<ChangeRec> reports = callbacks.get(cb); - ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName); + ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName, previousMode); if (changed != reports) { callbacks.put(cb, changed); } @@ -2580,11 +2588,13 @@ public class AppOpsService extends IAppOpsService.Stub { final int op; final int uid; final String pkg; + final int previous_mode; - ChangeRec(int _op, int _uid, String _pkg) { + ChangeRec(int _op, int _uid, String _pkg, int _previous_mode) { op = _op; uid = _uid; pkg = _pkg; + previous_mode = _previous_mode; } } @@ -2620,18 +2630,19 @@ public class AppOpsService extends IAppOpsService.Stub { for (int j = uidOpCount - 1; j >= 0; j--) { final int code = opModes.keyAt(j); if (AppOpsManager.opAllowsReset(code)) { + int previousMode = opModes.valueAt(j); opModes.removeAt(j); if (opModes.size() <= 0) { uidState.opModes = null; } for (String packageName : getPackagesForUid(uidState.uid)) { callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, - mOpModeWatchers.get(code)); + previousMode, mOpModeWatchers.get(code)); callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, - mPackageModeWatchers.get(packageName)); + previousMode, mPackageModeWatchers.get(packageName)); allChanges = addChange(allChanges, code, uidState.uid, - packageName); + packageName, previousMode); } } } @@ -2662,16 +2673,18 @@ public class AppOpsService extends IAppOpsService.Stub { Op curOp = pkgOps.valueAt(j); if (AppOpsManager.opAllowsReset(curOp.op) && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) { + int previousMode = curOp.mode; curOp.mode = AppOpsManager.opToDefaultMode(curOp.op); changed = true; uidChanged = true; final int uid = curOp.uidState.uid; callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, - mOpModeWatchers.get(curOp.op)); + previousMode, mOpModeWatchers.get(curOp.op)); callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, - mPackageModeWatchers.get(packageName)); + previousMode, mPackageModeWatchers.get(packageName)); - allChanges = addChange(allChanges, curOp.op, uid, packageName); + allChanges = addChange(allChanges, curOp.op, uid, packageName, + previousMode); curOp.removeAttributionsWithNoTime(); if (curOp.mAttributions.isEmpty()) { pkgOps.removeAt(j); @@ -2712,7 +2725,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < numChanges; i++) { ChangeRec change = allChanges.get(i); notifyOpChangedSync(change.op, change.uid, change.pkg, - AppOpsManager.opToDefaultMode(change.op)); + AppOpsManager.opToDefaultMode(change.op), change.previous_mode); } } } @@ -3400,7 +3413,19 @@ public class AppOpsService extends IAppOpsService.Stub { verifyIncomingOp(code); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { - return AppOpsManager.MODE_IGNORED; + return AppOpsManager.MODE_IGNORED; + } + + // As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution + // purposes and not as a check, also make sure that the caller is allowed to access + // the data gated by OP_RECORD_AUDIO. + // + // TODO: Revert this change before Android 12. + if (code == OP_RECORD_AUDIO_HOTWORD) { + int result = checkOperation(OP_RECORD_AUDIO, uid, packageName); + if (result != AppOpsManager.MODE_ALLOWED) { + return result; + } } RestrictionBypass bypass; diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 45f95fd3f663..074d3fe10816 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -224,12 +224,35 @@ import java.util.concurrent.atomic.AtomicBoolean; if (!addSpeakerphoneClient(cb, pid, on)) { return false; } + if (on) { + // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for startBluetoothScoForClient_Sync(). + mBtHelper.stopBluetoothScoForPid(pid); + } final boolean wasOn = isSpeakerphoneOn(); updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } + /** + * Turns speakerphone off for a given pid and update speakerphone state. + * @param pid + */ + @GuardedBy("mDeviceStateLock") + private void setSpeakerphoneOffForPid(int pid) { + SpeakerphoneClient client = getSpeakerphoneClientForPid(pid); + if (client == null) { + return; + } + client.unregisterDeathRecipient(); + mSpeakerphoneClients.remove(client); + final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(") + .append(pid).append(")").toString(); + updateSpeakerphoneOn(eventSource); + } + @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { @@ -476,8 +499,8 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } - /*package*/ void postSetModeOwnerPid(int pid) { - sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid); + /*package*/ void postSetModeOwnerPid(int pid, int mode) { + sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode); } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { @@ -488,6 +511,10 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { synchronized (mDeviceStateLock) { + // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for setSpeakerphoneOn(true). + setSpeakerphoneOffForPid(Binder.getCallingPid()); mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); } } @@ -949,7 +976,9 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mDeviceStateLock) { if (mModeOwnerPid != msg.arg1) { mModeOwnerPid = msg.arg1; - updateSpeakerphoneOn("setNewModeOwner"); + if (msg.arg2 != AudioSystem.MODE_RINGTONE) { + updateSpeakerphoneOn("setNewModeOwner"); + } if (mModeOwnerPid != 0) { mBtHelper.disconnectBluetoothSco(mModeOwnerPid); } @@ -1379,6 +1408,16 @@ import java.util.concurrent.atomic.AtomicBoolean; return false; } + @GuardedBy("mDeviceStateLock") + private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) { + for (SpeakerphoneClient cl : mSpeakerphoneClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // List of clients requesting speakerPhone ON @GuardedBy("mDeviceStateLock") private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients = diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java index 9ebd75bd0f64..af0e978726e3 100644 --- a/services/core/java/com/android/server/audio/AudioEventLogger.java +++ b/services/core/java/com/android/server/audio/AudioEventLogger.java @@ -60,7 +60,36 @@ public class AudioEventLogger { * @return the same instance of the event */ public Event printLog(String tag) { - Log.i(tag, eventToString()); + return printLog(ALOGI, tag); + } + + public static final int ALOGI = 0; + public static final int ALOGE = 1; + public static final int ALOGW = 2; + public static final int ALOGV = 3; + + /** + * Same as {@link #printLog(String)} with a log type + * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV} + * @param tag + * @return + */ + public Event printLog(int type, String tag) { + switch (type) { + case ALOGI: + Log.i(tag, eventToString()); + break; + case ALOGE: + Log.e(tag, eventToString()); + break; + case ALOGW: + Log.w(tag, eventToString()); + break; + case ALOGV: + default: + Log.v(tag, eventToString()); + break; + } return this; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c4eca605206d..7bb3e36d959e 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -26,6 +26,10 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; +import static com.android.server.audio.AudioEventLogger.Event.ALOGE; +import static com.android.server.audio.AudioEventLogger.Event.ALOGI; +import static com.android.server.audio.AudioEventLogger.Event.ALOGW; + import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; @@ -284,6 +288,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29; private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30; private static final int MSG_CHECK_MODE_FOR_UID = 31; + private static final int MSG_REINIT_VOLUMES = 32; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -673,6 +678,7 @@ public class AudioService extends IAudioService.Stub public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()")); mContext = context; mContentResolver = context.getContentResolver(); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); @@ -892,6 +898,9 @@ public class AudioService extends IAudioService.Stub mPrescaleAbsoluteVolume[i] = preScale[i]; } } + + // check on volume initialization + checkVolumeRangeInitialization("AudioService()"); } public void systemReady() { @@ -1019,11 +1028,15 @@ public class AudioService extends IAudioService.Stub if (!mSystemReady || (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) { Log.e(TAG, "Audioserver died."); + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "onAudioServerDied() audioserver died")); sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0, null, 500); return; } - Log.e(TAG, "Audioserver started."); + Log.i(TAG, "Audioserver started."); + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "onAudioServerDied() audioserver started")); updateAudioHalPids(); @@ -1058,14 +1071,7 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.setForceUse_Async(AudioSystem.FOR_SYSTEM, forSys, "onAudioServerDied"); // Restore stream volumes - int numStreamTypes = AudioSystem.getNumStreamTypes(); - for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - VolumeStreamState streamState = mStreamStates[streamType]; - AudioSystem.initStreamVolume( - streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10); - - streamState.applyAllVolumes(); - } + onReinitVolumes("after audioserver restart"); // Restore audio volume groups restoreVolumeGroups(); @@ -1163,6 +1169,72 @@ public class AudioService extends IAudioService.Stub setMicMuteFromSwitchInput(); } + private void onReinitVolumes(@NonNull String caller) { + final int numStreamTypes = AudioSystem.getNumStreamTypes(); + // keep track of any error during stream volume initialization + int status = AudioSystem.AUDIO_STATUS_OK; + for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { + VolumeStreamState streamState = mStreamStates[streamType]; + final int res = AudioSystem.initStreamVolume( + streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10); + if (res != AudioSystem.AUDIO_STATUS_OK) { + status = res; + Log.e(TAG, "Failed to initStreamVolume (" + res + ") for stream " + streamType); + // stream volume initialization failed, no need to try the others, it will be + // attempted again when MSG_REINIT_VOLUMES is handled + break; + } + streamState.applyAllVolumes(); + } + + // did it work? check based on status + if (status != AudioSystem.AUDIO_STATUS_OK) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume failed with " + status + " will retry") + .printLog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + return; + } + + // did it work? check based on min/max values of some basic streams + if (!checkVolumeRangeInitialization(caller)) { + return; + } + + // success + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume succeeded").printLog(ALOGI, TAG)); + } + + /** + * Check volume ranges were properly initialized + * @return true if volume ranges were successfully initialized + */ + private boolean checkVolumeRangeInitialization(String caller) { + boolean success = true; + final int[] basicStreams = { AudioSystem.STREAM_ALARM, AudioSystem.STREAM_RING, + AudioSystem.STREAM_MUSIC, AudioSystem.STREAM_VOICE_CALL, + AudioSystem.STREAM_ACCESSIBILITY }; + for (int streamType : basicStreams) { + final AudioAttributes aa = new AudioAttributes.Builder() + .setInternalLegacyStreamType(streamType).build(); + if (AudioSystem.getMaxVolumeIndexForAttributes(aa) < 0 + || AudioSystem.getMinVolumeIndexForAttributes(aa) < 0) { + success = false; + break; + } + } + if (!success) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume succeeded but invalid mix/max levels, will retry") + .printLog(ALOGW, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } + return success; + } + private void onDispatchAudioServerStateChange(boolean state) { synchronized (mAudioServerStateListeners) { for (AsdProxy asdp : mAudioServerStateListeners.values()) { @@ -3680,13 +3752,15 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; - private String mPackage; + private final boolean mIsPrivileged; + private final String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client - SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { + SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { mCb = cb; mPid = pid; mUid = uid; + mIsPrivileged = isPrivileged; mPackage = caller; } @@ -3698,12 +3772,13 @@ public class AudioService extends IAudioService.Stub if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); } else { - newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG); + newModeOwnerPid = setModeInt( + AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG); } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode()); } public int getPid() { @@ -3729,6 +3804,10 @@ public class AudioService extends IAudioService.Stub public String getPackage() { return mPackage; } + + public boolean isPrivileged() { + return mIsPrivileged; + } } /** @see AudioManager#setMode(int) */ @@ -3780,18 +3859,19 @@ public class AudioService extends IAudioService.Stub + " without permission or being mode owner"); return; } - newModeOwnerPid = setModeInt( - mode, cb, callingPid, Binder.getCallingUid(), callingPackage); + newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(), + hasModifyPhoneStatePermission, callingPackage); } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode()); } // setModeInt() returns a valid PID if the audio mode was successfully set to // any mode other than NORMAL. @GuardedBy("mDeviceBroker.mSetModeLock") - private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) { + private int setModeInt( + int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", uid=" + uid + ", caller=" + caller + ")"); @@ -3843,7 +3923,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid, caller); + hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller); } // Register for client death notification try { @@ -3902,7 +3982,8 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); - if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { + if (actualMode == AudioSystem.MODE_IN_COMMUNICATION + && !hdlr.isPrivileged()) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, @@ -5570,7 +5651,15 @@ public class AudioService extends IAudioService.Stub mIndexMin = MIN_STREAM_VOLUME[streamType] * 10; mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex() mIndexMax = MAX_STREAM_VOLUME[streamType] * 10; - AudioSystem.initStreamVolume(streamType, mIndexMin / 10, mIndexMax / 10); + final int status = AudioSystem.initStreamVolume( + streamType, mIndexMin / 10, mIndexMax / 10); + if (status != AudioSystem.AUDIO_STATUS_OK) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "VSS() stream:" + streamType + " initStreamVolume=" + status) + .printLog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } readSettings(); mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION); @@ -6401,14 +6490,17 @@ public class AudioService extends IAudioService.Stub if (msg.obj == null) { break; } - // If the app corresponding to this mode death handler object is not - // capturing or playing audio anymore after 3 seconds, remove it - // from the stack. Otherwise, check again in 3 seconds. + // If no other app is currently owning the audio mode and + // the app corresponding to this mode death handler object is still in the + // mode owner stack but not capturing or playing audio after 3 seconds, + // remove it from the stack. + // Otherwise, check again in 3 seconds. SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; if (mSetModeDeathHandlers.indexOf(h) < 0) { break; } - if (mRecordMonitor.isRecordingActiveForUid(h.getUid()) + if (getModeOwnerUid() != h.getUid() + || mRecordMonitor.isRecordingActiveForUid(h.getUid()) || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, @@ -6419,11 +6511,15 @@ public class AudioService extends IAudioService.Stub CHECK_MODE_FOR_UID_PERIOD_MS); break; } - // For now just log the fact that an app is hogging the audio mode. - // TODO(b/160260850): remove abusive app from audio mode stack. + setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), + h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; + + case MSG_REINIT_VOLUMES: + onReinitVolumes((String) msg.obj); + break; } } } @@ -7354,12 +7450,16 @@ public class AudioService extends IAudioService.Stub //========================================================================================== // AudioService logging and dumpsys //========================================================================================== + static final int LOG_NB_EVENTS_LIFECYCLE = 20; static final int LOG_NB_EVENTS_PHONE_STATE = 20; static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30; static final int LOG_NB_EVENTS_FORCE_USE = 20; static final int LOG_NB_EVENTS_VOLUME = 40; static final int LOG_NB_EVENTS_DYN_POLICY = 10; + static final AudioEventLogger sLifecycleLogger = new AudioEventLogger(LOG_NB_EVENTS_LIFECYCLE, + "audio services lifecycle"); + final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE, "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))"); @@ -7436,6 +7536,7 @@ public class AudioService extends IAudioService.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + sLifecycleLogger.dump(pw); if (mAudioHandler != null) { pw.println("\nMessage handler (watch for unhandled messages):"); mAudioHandler.dump(new PrintWriterPrinter(pw), " "); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index b4c41b274dbe..7616557ac80f 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -432,19 +432,36 @@ public class BtHelper { // and this must be done on behalf of system server to make sure permissions are granted. final long ident = Binder.clearCallingIdentity(); if (client != null) { - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, - SCO_MODE_VIRTUAL_CALL); - // If a disconnection is pending, the client will be removed whne clearAllScoClients() - // is called form receiveBtEvent() - if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ - && mScoAudioState != SCO_STATE_DEACTIVATING) { - client.remove(false /*stop */, true /*unregister*/); - } + stopAndRemoveClient(client, eventSource); } Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void stopBluetoothScoForPid(int pid) { + ScoClient client = getScoClientForPid(pid); + if (client == null) { + return; + } + final String eventSource = new StringBuilder("stopBluetoothScoForPid(") + .append(pid).append(")").toString(); + stopAndRemoveClient(client, eventSource); + } + + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + // @GuardedBy("BtHelper.this") + private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + // If a disconnection is pending, the client will be removed when clearAllScoClients() + // is called form receiveBtEvent() + if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATING) { + client.remove(false /*stop */, true /*unregister*/); + } + } /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { if (mHearingAid == null) { @@ -974,6 +991,16 @@ public class BtHelper { return null; } + @GuardedBy("BtHelper.this") + private ScoClient getScoClientForPid(int pid) { + for (ScoClient cl : mScoClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1c93d4eb599b..347beab0c42f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -77,6 +77,7 @@ import android.net.ipsec.ike.ChildSessionParams; import android.net.ipsec.ike.IkeSession; import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -142,6 +143,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; /** @@ -2300,7 +2302,7 @@ public class Vpn { void onChildTransformCreated( @NonNull Network network, @NonNull IpSecTransform transform, int direction); - void onSessionLost(@NonNull Network network); + void onSessionLost(@NonNull Network network, @Nullable Exception exception); } /** @@ -2457,7 +2459,7 @@ public class Vpn { networkAgent.sendLinkProperties(lp); } catch (Exception e) { Log.d(TAG, "Error in ChildOpened for network " + network, e); - onSessionLost(network); + onSessionLost(network, e); } } @@ -2487,7 +2489,7 @@ public class Vpn { mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform); } catch (IOException e) { Log.d(TAG, "Transform application failed for network " + network, e); - onSessionLost(network); + onSessionLost(network, e); } } @@ -2545,11 +2547,20 @@ public class Vpn { Log.d(TAG, "Ike Session started for network " + network); } catch (Exception e) { Log.i(TAG, "Setup failed for network " + network + ". Aborting", e); - onSessionLost(network); + onSessionLost(network, e); } }); } + /** Marks the state as FAILED, and disconnects. */ + private void markFailedAndDisconnect(Exception exception) { + synchronized (Vpn.this) { + updateState(DetailedState.FAILED, exception.getMessage()); + } + + disconnectVpnRunner(); + } + /** * Handles loss of a session * @@ -2559,7 +2570,7 @@ public class Vpn { * <p>This method MUST always be called on the mExecutor thread in order to ensure * consistency of the Ikev2VpnRunner fields. */ - public void onSessionLost(@NonNull Network network) { + public void onSessionLost(@NonNull Network network, @Nullable Exception exception) { if (!isActiveNetwork(network)) { Log.d(TAG, "onSessionLost() called for obsolete network " + network); @@ -2571,6 +2582,27 @@ public class Vpn { return; } + if (exception instanceof IkeProtocolException) { + final IkeProtocolException ikeException = (IkeProtocolException) exception; + + switch (ikeException.getErrorType()) { + case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough + case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough + case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE: + // All the above failures are configuration errors, and are terminal + markFailedAndDisconnect(exception); + return; + // All other cases possibly recoverable. + } + } else if (exception instanceof IllegalArgumentException) { + // Failed to build IKE/ChildSessionParams; fatal profile configuration error + markFailedAndDisconnect(exception); + return; + } + mActiveNetwork = null; // Close all obsolete state, but keep VPN alive incase a usable network comes up. @@ -2620,12 +2652,18 @@ public class Vpn { } /** - * Cleans up all Ikev2VpnRunner internal state + * Disconnects and shuts down this VPN. + * + * <p>This method resets all internal Ikev2VpnRunner state, but unless called via + * VpnRunner#exit(), this Ikev2VpnRunner will still be listed as the active VPN of record + * until the next VPN is started, or the Ikev2VpnRunner is explicitly exited. This is + * necessary to ensure that the detailed state is shown in the Settings VPN menus; if the + * active VPN is cleared, Settings VPNs will not show the resultant state or errors. * * <p>This method MUST always be called on the mExecutor thread in order to ensure * consistency of the Ikev2VpnRunner fields. */ - private void shutdownVpnRunner() { + private void disconnectVpnRunner() { mActiveNetwork = null; mIsRunning = false; @@ -2639,9 +2677,13 @@ public class Vpn { @Override public void exitVpnRunner() { - mExecutor.execute(() -> { - shutdownVpnRunner(); - }); + try { + mExecutor.execute(() -> { + disconnectVpnRunner(); + }); + } catch (RejectedExecutionException ignored) { + // The Ikev2VpnRunner has already shut down. + } } } diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java index 103f659cc258..626303001ba0 100644 --- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java +++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java @@ -270,13 +270,13 @@ public class VpnIkev2Utils { @Override public void onClosed() { Log.d(mTag, "IkeClosed for network " + mNetwork); - mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry? + mCallback.onSessionLost(mNetwork, null); // Server requested session closure. Retry? } @Override public void onClosedExceptionally(@NonNull IkeException exception) { Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception); - mCallback.onSessionLost(mNetwork); + mCallback.onSessionLost(mNetwork, exception); } @Override @@ -306,13 +306,13 @@ public class VpnIkev2Utils { @Override public void onClosed() { Log.d(mTag, "ChildClosed for network " + mNetwork); - mCallback.onSessionLost(mNetwork); + mCallback.onSessionLost(mNetwork, null); } @Override public void onClosedExceptionally(@NonNull IkeException exception) { Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception); - mCallback.onSessionLost(mNetwork); + mCallback.onSessionLost(mNetwork, exception); } @Override @@ -349,7 +349,7 @@ public class VpnIkev2Utils { @Override public void onLost(@NonNull Network network) { Log.d(mTag, "Tearing down; lost network: " + network); - mCallback.onSessionLost(network); + mCallback.onSessionLost(network, null); } } diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 2672f848f192..cf03c607a368 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -948,7 +948,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem if (debug) { Slog.d(mTag, "Eagerly recreating service for user " + userId); } - getServiceForUserLocked(userId); + updateCachedServiceLocked(userId); } } onServicePackageRestartedLocked(userId); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 254285dfbd41..bcfbcd124e71 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1700,7 +1700,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER) .setPackage(mContext.getPackageName()); - mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); + mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, + PendingIntent.FLAG_IMMUTABLE); mShowOngoingImeSwitcherForPhones = false; @@ -2529,7 +2530,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( - mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); + mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), + PendingIntent.FLAG_IMMUTABLE)); if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { mLastBindTime = SystemClock.uptimeMillis(); @@ -3273,6 +3275,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + if (mCurClient == null || mCurClient.curSession == null) { + return false; + } if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested || mShowForced)) { if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); @@ -3457,7 +3462,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // pre-rendering not supported on low-ram devices. cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam; - if (mCurFocusedWindow == windowToken) { + final boolean sameWindowFocused = mCurFocusedWindow == windowToken; + final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; + final boolean startInputByWinGainedFocus = + (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0; + + if (sameWindowFocused && isTextEditor) { if (DEBUG) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client + " attribute=" + attribute + ", token = " + windowToken @@ -3472,6 +3482,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, null, null, null, -1, null); } + mCurFocusedWindow = windowToken; mCurFocusedWindowSoftInputMode = softInputMode; mCurFocusedWindowClient = cs; @@ -3489,7 +3500,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == LayoutParams.SOFT_INPUT_ADJUST_RESIZE || mRes.getConfiguration().isLayoutSizeAtLeast( Configuration.SCREENLAYOUT_SIZE_LARGE); - final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; // We want to start input before showing the IME, but after closing // it. We want to do this after closing it to help the IME disappear @@ -3500,7 +3510,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult res = null; switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) { case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (!isTextEditor || !doAutoShow) { + if (!sameWindowFocused && (!isTextEditor || !doAutoShow)) { if (LayoutParams.mayUseInputMethod(windowFlags)) { // There is no focus view, and this window will // be behind any soft input window, so hide the @@ -3549,9 +3559,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } break; case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: - if (DEBUG) Slog.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(mCurFocusedWindow, 0, null, - SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); + if (!sameWindowFocused) { + if (DEBUG) Slog.v(TAG, "Window asks to hide input"); + hideCurrentInputLocked(mCurFocusedWindow, 0, null, + SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); + } break; case LayoutParams.SOFT_INPUT_STATE_VISIBLE: if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { @@ -3576,13 +3588,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Window asks to always show input"); if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( unverifiedTargetSdkVersion, startInputFlags)) { - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, - attribute, startInputFlags, startInputReason); - didStart = true; + if (!sameWindowFocused) { + if (attribute != null) { + res = startInputUncheckedLocked(cs, inputContext, missingMethods, + attribute, startInputFlags, startInputReason); + didStart = true; + } + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE); } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, - SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE); } else { Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3593,7 +3607,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!didStart) { if (attribute != null) { - if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value() + if (sameWindowFocused) { + // On previous platforms, when Dialogs re-gained focus, the Activity behind + // would briefly gain focus first, and dismiss the IME. + // On R that behavior has been fixed, but unfortunately apps have come + // to rely on this behavior to hide the IME when the editor no longer has focus + // To maintain compatibility, we are now hiding the IME when we don't have + // an editor upon refocusing a window. + if (startInputByWinGainedFocus) { + hideCurrentInputLocked(mCurFocusedWindow, 0, null, + SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR); + } + res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, + startInputFlags, startInputReason); + } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value() || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) { res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, startInputFlags, startInputReason); @@ -3607,6 +3634,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return res; } + private boolean isImeVisible() { + return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0; + } + private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. final int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 817902d9d566..b61c6a7ca569 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -205,8 +205,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format("Successfully pushed rule set: %s", version)); + Slog.i( + TAG, + String.format( + "Successfully pushed rule set to version '%s' from '%s'", + version, ruleProvider)); } + FrameworkStatsLog.write( FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success, @@ -324,13 +329,12 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { + getAllowedInstallers(packageInfo)); } IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); - if (DEBUG_INTEGRITY_COMPONENT) { + if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { Slog.i( TAG, - "Integrity check result: " - + result.getEffect() - + " due to " - + result.getMatchedRules()); + String.format( + "Integrity check of %s result: %s due to %s", + packageName, result.getEffect(), result.getMatchedRules())); } FrameworkStatsLog.write( @@ -673,8 +677,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages. List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format( - "Rule provider system app list contains: %s", allowedRuleProviders)); + Slog.i( + TAG, + String.format( + "Rule provider system app list contains: %s", allowedRuleProviders)); } // Identify the package names in the caller list. @@ -730,9 +736,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private boolean integrityCheckIncludesRuleProvider() { return Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) + mContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + 0) == 1; } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 9adde24a99e0..5c9350a8452c 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -2064,10 +2064,12 @@ public class LocationManagerService extends ILocationManager.Stub { } } + @Nullable @Override - public boolean getCurrentLocation(LocationRequest locationRequest, - ICancellationSignal remoteCancellationSignal, ILocationListener listener, - String packageName, String featureId, String listenerId) { + public ICancellationSignal getCurrentLocation(LocationRequest locationRequest, + ILocationListener listener, String packageName, String featureId, String listenerId) { + ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport(); + // side effect of validating locationRequest and packageName Location lastLocation = getLastLocation(locationRequest, packageName, featureId); if (lastLocation != null) { @@ -2077,17 +2079,17 @@ public class LocationManagerService extends ILocationManager.Stub { if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { try { listener.onLocationChanged(lastLocation); - return true; + return remoteCancellationSignal; } catch (RemoteException e) { Log.w(TAG, e); - return false; + return null; } } if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) { if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) { // not allowed to request new locations, so we can't return anything - return false; + return null; } } } @@ -2095,11 +2097,8 @@ public class LocationManagerService extends ILocationManager.Stub { requestLocationUpdates(locationRequest, listener, null, packageName, featureId, listenerId); CancellationSignal cancellationSignal = CancellationSignal.fromTransport( remoteCancellationSignal); - if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener( - () -> removeUpdates(listener, null)); - } - return true; + cancellationSignal.setOnCancelListener(() -> removeUpdates(listener, null)); + return remoteCancellationSignal; } @Override diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 90370ddd21dd..69b02ceb2411 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2620,7 +2620,6 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId); final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid( getGateKeeperService(), credentialHash, credential, userId); - onAuthTokenKnownForUser(userId, auth); if (auth == null) { Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token"); return null; @@ -2643,6 +2642,7 @@ public class LockSettingsService extends ILockSettings.Stub { } fixateNewestUserKeyAuth(userId); setSyntheticPasswordHandleLocked(handle, userId); + onAuthTokenKnownForUser(userId, auth); return auth; } diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java index 9dae1b44117b..6e655eafa0e9 100644 --- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java +++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java @@ -259,7 +259,7 @@ final class MediaButtonReceiverHolder { return ""; } return String.join(COMPONENT_NAME_USER_ID_DELIM, - mComponentName.toString(), + mComponentName.flattenToString(), String.valueOf(mUserId), String.valueOf(mComponentType)); } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 9b356f0e8eef..803eb862e0f1 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -1937,7 +1937,8 @@ public class MediaSessionService extends SystemService implements Monitor { // Context#getPackageName() for getting package name that matches with the PID/UID, // but it doesn't tell which package has created the MediaController, so useless. return hasMediaControlPermission(controllerPid, controllerUid) - || hasEnabledNotificationListener(userId, controllerPackageName); + || hasEnabledNotificationListener( + userId, controllerPackageName, controllerUid); } finally { Binder.restoreCallingIdentity(token); } @@ -2001,29 +2002,29 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } - private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName) - throws RemoteException { - // You may not access another user's content as an enabled listener. - final int userId = UserHandle.getUserId(resolvedUserId); - if (resolvedUserId != userId) { + private boolean hasEnabledNotificationListener(int callingUserId, + String controllerPackageName, int controllerUid) throws RemoteException { + int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier(); + if (callingUserId != controllerUserId) { + // Enabled notification listener only works within the same user. return false; } // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( // String pkgName) to notification team for optimization final List<ComponentName> enabledNotificationListeners = - mNotificationManager.getEnabledNotificationListeners(userId); + mNotificationManager.getEnabledNotificationListeners(controllerUserId); if (enabledNotificationListeners != null) { for (int i = 0; i < enabledNotificationListeners.size(); i++) { - if (TextUtils.equals(packageName, + if (TextUtils.equals(controllerPackageName, enabledNotificationListeners.get(i).getPackageName())) { return true; } } } if (DEBUG) { - Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled " - + "notification listener"); + Log.d(TAG, controllerPackageName + " (uid=" + controllerUid + + ") doesn't have an enabled notification listener"); } return false; } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index d6557f6410ec..b3eb53116d49 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -96,7 +96,9 @@ import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; +import static com.android.internal.util.XmlUtils.readThisIntArrayXml; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; +import static com.android.internal.util.XmlUtils.writeIntArrayXml; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; @@ -229,6 +231,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.StatLogger; +import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -239,6 +242,7 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.File; @@ -313,7 +317,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int VERSION_ADDED_NETWORK_ID = 9; private static final int VERSION_SWITCH_UID = 10; private static final int VERSION_ADDED_CYCLE = 11; - private static final int VERSION_LATEST = VERSION_ADDED_CYCLE; + private static final int VERSION_ADDED_NETWORK_TYPES = 12; + private static final int VERSION_LATEST = VERSION_ADDED_NETWORK_TYPES; @VisibleForTesting public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING; @@ -332,6 +337,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG_WHITELIST = "whitelist"; private static final String TAG_RESTRICT_BACKGROUND = "restrict-background"; private static final String TAG_REVOKED_RESTRICT_BACKGROUND = "revoked-restrict-background"; + private static final String TAG_XML_UTILS_INT_ARRAY = "int-array"; private static final String ATTR_VERSION = "version"; private static final String ATTR_RESTRICT_BACKGROUND = "restrictBackground"; @@ -360,6 +366,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String ATTR_USAGE_BYTES = "usageBytes"; private static final String ATTR_USAGE_TIME = "usageTime"; private static final String ATTR_OWNER_PACKAGE = "ownerPackage"; + private static final String ATTR_NETWORK_TYPES = "networkTypes"; + private static final String ATTR_XML_UTILS_NAME = "name"; private static final String ACTION_ALLOW_BACKGROUND = "com.android.server.net.action.ALLOW_BACKGROUND"; @@ -2311,13 +2319,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } final int subId = readIntAttribute(in, ATTR_SUB_ID); + final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE); + + if (version >= VERSION_ADDED_NETWORK_TYPES) { + final int depth = in.getDepth(); + while (XmlUtils.nextElementWithin(in, depth)) { + if (TAG_XML_UTILS_INT_ARRAY.equals(in.getName()) + && ATTR_NETWORK_TYPES.equals( + readStringAttribute(in, ATTR_XML_UTILS_NAME))) { + final int[] networkTypes = + readThisIntArrayXml(in, TAG_XML_UTILS_INT_ARRAY, null); + builder.setNetworkTypes(networkTypes); + } + } + } + final SubscriptionPlan plan = builder.build(); mSubscriptionPlans.put(subId, ArrayUtils.appendElement( SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan)); - - final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE); mSubscriptionPlansOwner.put(subId, ownerPackage); - } else if (TAG_UID_POLICY.equals(tag)) { final int uid = readIntAttribute(in, ATTR_UID); final int policy = readIntAttribute(in, ATTR_POLICY); @@ -2513,6 +2533,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior()); writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes()); writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime()); + try { + writeIntArrayXml(plan.getNetworkTypes(), ATTR_NETWORK_TYPES, out); + } catch (XmlPullParserException ignored) { } out.endTag(null, TAG_SUBSCRIPTION_PLAN); } } @@ -3310,7 +3333,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // let in core system components (like the Settings app). final String ownerPackage = mSubscriptionPlansOwner.get(subId); if (Objects.equals(ownerPackage, callingPackage) - || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)) { + || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID) + || (UserHandle.getCallingAppId() == android.os.Process.PHONE_UID)) { return mSubscriptionPlans.get(subId); } else { Log.w(TAG, "Not returning plans because caller " + callingPackage diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f8d54adbeb5b..5d0981da1906 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1939,6 +1939,13 @@ public class NotificationManagerService extends SystemService { } @Override + void onConsolidatedPolicyChanged() { + Binder.withCleanCallingIdentity(() -> { + mRankingHandler.requestSort(); + }); + } + + @Override void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) { Binder.withCleanCallingIdentity(() -> { Intent intent = new Intent(ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED); @@ -6238,6 +6245,8 @@ public class NotificationManagerService extends SystemService { for (NotificationRecord r : enqueued) { if (r.mUpdateTimeMs > mWhen) { // At least one enqueue was posted after the cancel, so we're invalid + Slog.i(TAG, "notification cancel ignored due to newer enqueued entry" + + "key=" + r.getSbn().getKey()); return; } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 22d6c9909edf..10f77144e022 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -567,9 +567,9 @@ public class AppsFilter { final boolean newIsForceQueryable = mForceQueryable.contains(newPkgSetting.appId) /* shared user that is already force queryable */ - || newPkg.isForceQueryable() - || newPkgSetting.forceQueryableOverride + || newPkgSetting.forceQueryableOverride /* adb override */ || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() || ArrayUtils.contains(mForceQueryableByDevicePackageNames, newPkg.getPackageName()))); if (newIsForceQueryable @@ -646,24 +646,74 @@ public class AppsFilter { } } - private void updateEntireShouldFilterCacheAsync() { - mBackgroundExecutor.execute(this::updateEntireShouldFilterCache); - } - private void updateEntireShouldFilterCache() { mStateProvider.runWithState((settings, users) -> { SparseArray<SparseBooleanArray> cache = - new SparseArray<>(users.length * settings.size()); - for (int i = settings.size() - 1; i >= 0; i--) { - updateShouldFilterCacheForPackage(cache, - null /*skipPackage*/, settings.valueAt(i), settings, users, i); - } + updateEntireShouldFilterCacheInner(settings, users); synchronized (mCacheLock) { mShouldFilterCache = cache; } }); } + private SparseArray<SparseBooleanArray> updateEntireShouldFilterCacheInner( + ArrayMap<String, PackageSetting> settings, UserInfo[] users) { + SparseArray<SparseBooleanArray> cache = + new SparseArray<>(users.length * settings.size()); + for (int i = settings.size() - 1; i >= 0; i--) { + updateShouldFilterCacheForPackage(cache, + null /*skipPackage*/, settings.valueAt(i), settings, users, i); + } + return cache; + } + + private void updateEntireShouldFilterCacheAsync() { + mBackgroundExecutor.execute(() -> { + final ArrayMap<String, PackageSetting> settingsCopy = new ArrayMap<>(); + final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>(); + final UserInfo[][] usersRef = new UserInfo[1][]; + mStateProvider.runWithState((settings, users) -> { + packagesCache.ensureCapacity(settings.size()); + settingsCopy.putAll(settings); + usersRef[0] = users; + // store away the references to the immutable packages, since settings are retained + // during updates. + for (int i = 0, max = settings.size(); i < max; i++) { + final AndroidPackage pkg = settings.valueAt(i).pkg; + packagesCache.put(settings.keyAt(i), pkg); + } + }); + SparseArray<SparseBooleanArray> cache = + updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]); + boolean[] changed = new boolean[1]; + // We have a cache, let's make sure the world hasn't changed out from under us. + mStateProvider.runWithState((settings, users) -> { + if (settings.size() != settingsCopy.size()) { + changed[0] = true; + return; + } + for (int i = 0, max = settings.size(); i < max; i++) { + final AndroidPackage pkg = settings.valueAt(i).pkg; + if (!Objects.equals(pkg, packagesCache.get(settings.keyAt(i)))) { + changed[0] = true; + return; + } + } + }); + if (changed[0]) { + // Something has changed, just update the cache inline with the lock held + updateEntireShouldFilterCache(); + if (DEBUG_LOGGING) { + Slog.i(TAG, "Rebuilding cache with lock due to package change."); + } + } else { + synchronized (mCacheLock) { + mShouldFilterCache = cache; + } + } + }); + } + public void onUsersChanged() { synchronized (mCacheLock) { if (mShouldFilterCache != null) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 150fdaa10929..dd7ef5b02877 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -280,6 +280,7 @@ import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; import android.permission.IPermissionManager; +import android.provider.ContactsContract; import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -2991,7 +2992,10 @@ public class PackageManagerService extends IPackageManager.Stub t.traceEnd(); t.traceBegin("read user settings"); - mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false)); + mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers( + /* excludePartial= */ true, + /* excludeDying= */ false, + /* excludePreCreated= */ false)); t.traceEnd(); // Clean up orphaned packages for which the code path doesn't exist @@ -4201,13 +4205,9 @@ public class PackageManagerService extends IPackageManager.Stub Iterator<ResolveInfo> iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); - final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); - if (ps != null) { - final PermissionsState permissionsState = ps.getPermissionsState(); - if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) - || Build.IS_ENG) { - continue; - } + if (checkPermission(Manifest.permission.INSTALL_PACKAGES, + rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) { + continue; } iter.remove(); } @@ -4383,8 +4383,24 @@ public class PackageManagerService extends IPackageManager.Stub final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); // Compute granted permissions only if package has requested permissions - final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions()) + Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions()) ? Collections.emptySet() : permissionsState.getPermissions(userId); + if (state.instantApp) { + permissions = new ArraySet<>(permissions); + permissions.removeIf(permissionName -> { + BasePermission permission = mPermissionManager.getPermissionTEMP( + permissionName); + if (permission == null) { + return true; + } + if (!permission.isInstant()) { + EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId, + ps.appId), permissionName); + return true; + } + return false; + }); + } PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags, ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps); @@ -8582,10 +8598,9 @@ public class PackageManagerService extends IPackageManager.Stub private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; - final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i<permissions.length; i++) { final String permission = permissions[i]; - if (permissionsState.hasPermission(permission, userId)) { + if (checkPermission(permission, ps.name, userId) == PERMISSION_GRANTED) { tmp[i] = true; numMatch++; } else { @@ -11100,6 +11115,8 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(), originalPkgSetting.name); mTransferredPackages.add(originalPkgSetting.name); + } else { + mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName()); } } if (pkgSetting.sharedUser != null) { @@ -19190,6 +19207,14 @@ public class PackageManagerService extends IPackageManager.Stub final int flags = action.flags; final boolean systemApp = isSystemApp(ps); + // We need to get the permission state before package state is (potentially) destroyed. + final SparseBooleanArray hadSuspendAppsPermission = new SparseBooleanArray(); + // allUserHandles could be null, so call mUserManager.getUserIds() directly which is cached anyway. + for (int userId : mUserManager.getUserIds()) { + hadSuspendAppsPermission.put(userId, checkPermission(Manifest.permission.SUSPEND_APPS, + packageName, userId) == PERMISSION_GRANTED); + } + final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) @@ -19256,8 +19281,7 @@ public class PackageManagerService extends IPackageManager.Stub affectedUserIds = resolveUserIds(userId); } for (final int affectedUserId : affectedUserIds) { - if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, - affectedUserId)) { + if (hadSuspendAppsPermission.get(affectedUserId)) { unsuspendForSuspendingPackage(packageName, affectedUserId); removeAllDistractingPackageRestrictions(affectedUserId); } @@ -21033,8 +21057,8 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.setEnabled(newState, userId, callingPackage); if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER || newState == COMPONENT_ENABLED_STATE_DISABLED) - && pkgSetting.getPermissionsState().hasPermission( - Manifest.permission.SUSPEND_APPS, userId)) { + && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) + == PERMISSION_GRANTED) { // This app should not generally be allowed to get disabled by the UI, but if it // ever does, we don't want to end up with some of the user's apps permanently // suspended. @@ -21429,6 +21453,8 @@ public class PackageManagerService extends IPackageManager.Stub .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL); co.onChange(true); + mAppsFilter.onSystemReady(); + // Disable any carrier apps. We do this very early in boot to prevent the apps from being // disabled after already being started. CarrierAppUtils.disableCarrierAppsUntilPrivileged( @@ -21577,8 +21603,6 @@ public class PackageManagerService extends IPackageManager.Stub mInstallerService.restoreAndApplyStagedSessionIfNeeded(); mExistingPackages = null; - - mAppsFilter.onSystemReady(); } public void waitForAppDataPrepared() { @@ -25405,6 +25429,32 @@ public class PackageManagerService extends IPackageManager.Stub } } + @Override + public void grantImplicitAccess(int recipientUid, String visibleAuthority) { + // This API is exposed temporarily to only the contacts provider. (b/158688602) + final int callingUid = Binder.getCallingUid(); + ProviderInfo contactsProvider = resolveContentProviderInternal( + ContactsContract.AUTHORITY, 0, UserHandle.USER_SYSTEM); + if (contactsProvider == null || contactsProvider.applicationInfo == null + || !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) { + throw new SecurityException(callingUid + " is not allow to call grantImplicitAccess"); + } + final int userId = UserHandle.getUserId(recipientUid); + final long token = Binder.clearCallingIdentity(); + final ProviderInfo providerInfo; + try { + providerInfo = resolveContentProvider(visibleAuthority, 0 /*flags*/, userId); + } finally { + Binder.restoreCallingIdentity(token); + } + if (providerInfo == null) { + return; + } + int visibleUid = providerInfo.applicationInfo.uid; + mPmInternal.grantImplicitAccess(userId, null /*Intent*/, UserHandle.getAppId(recipientUid), + visibleUid, false); + } + boolean canHaveOatDir(String packageName) { synchronized (mLock) { AndroidPackage p = mPackages.get(packageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 8bbe9cc01ada..7b4e4d94baa2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2313,7 +2313,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isVendorApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isVendor(); } catch (RemoteException e) { return false; @@ -2322,7 +2323,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isProductApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isProduct(); } catch (RemoteException e) { return false; @@ -2331,7 +2333,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isSystemExtApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isSystemExt(); } catch (RemoteException e) { return false; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index a0feb94fd3fc..a7164f94f66b 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -476,6 +476,10 @@ public final class Settings { return mRenamedPackages.put(pkgName, origPkgName); } + void removeRenamedPackageLPw(String pkgName) { + mRenamedPackages.remove(pkgName); + } + public boolean canPropagatePermissionToInstantApp(String permName) { return mPermissions.canPropagatePermissionToInstantApp(permName); } @@ -1613,6 +1617,7 @@ public final class Settings { return; } str = new FileInputStream(userPackagesStateFile); + if (DEBUG_MU) Log.i(TAG, "Reading " + userPackagesStateFile); } final XmlPullParser parser = Xml.newPullParser(); parser.setInput(str, StandardCharsets.UTF_8.name()); @@ -2057,9 +2062,13 @@ public final class Settings { serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS); + if (DEBUG_MU) Log.i(TAG, "Writing " + userPackagesStateFile); for (final PackageSetting pkg : mPackages.values()) { final PackageUserState ustate = pkg.readUserState(userId); - if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled); + if (DEBUG_MU) { + Log.i(TAG, " pkg=" + pkg.name + ", installed=" + ustate.installed + + ", state=" + ustate.enabled); + } serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, pkg.name); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 5b1c0fdb8249..600357b7697f 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -743,7 +743,6 @@ public class StagingManager { PackageInstaller.SessionParams params = originalSession.params.copy(); params.isStaged = false; params.installFlags |= PackageManager.INSTALL_STAGED; - // TODO(b/129744602): use the userid from the original session. if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; params.installFlags |= PackageManager.INSTALL_DRY_RUN; @@ -753,7 +752,7 @@ public class StagingManager { try { int apkSessionId = mPi.createSession( params, originalSession.getInstallerPackageName(), - 0 /* UserHandle.SYSTEM */); + originalSession.userId); PackageInstallerSession apkSession = mPi.getSession(apkSessionId); apkSession.open(); for (int i = 0, size = apkFilePaths.size(); i < size; i++) { @@ -811,10 +810,9 @@ public class StagingManager { if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } - // TODO(b/129744602): use the userid from the original session. final int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), - 0 /* UserHandle.SYSTEM */); + session.userId); final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId); try { apkParentSession.open(); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 27924a68ff48..f5d7d9eda1ba 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -114,7 +114,6 @@ import com.android.server.SystemService; import com.android.server.am.UserState; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.utils.TimingsTraceAndSlog; -import com.android.server.wm.ActivityTaskManagerInternal; import libcore.io.IoUtils; @@ -134,6 +133,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -173,6 +173,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PARTIAL = "partial"; private static final String ATTR_PRE_CREATED = "preCreated"; + private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; @@ -406,6 +407,10 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private int[] mUserIds; + + @GuardedBy("mUsersLock") + private int[] mUserIdsIncludingPreCreated; + @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; @@ -2480,16 +2485,35 @@ public class UserManagerService extends IUserManager.Stub { } /** - * Returns an array of user ids. This array is cached here for quick access, so do not modify or - * cache it elsewhere. + * Returns an array of user ids. + * + * <p>This array is cached here for quick access, so do not modify or cache it elsewhere. + * * @return the array of user ids. */ - public int[] getUserIds() { + public @NonNull int[] getUserIds() { synchronized (mUsersLock) { return mUserIds; } } + /** + * Returns an array of user ids, including pre-created users. + * + * <p>This method should only used for the specific cases that need to handle pre-created users; + * most callers should call {@link #getUserIds()} instead. + * + * <p>This array is cached here for quick access, so do not modify or + * cache it elsewhere. + * + * @return the array of user ids. + */ + public @NonNull int[] getUserIdsIncludingPreCreated() { + synchronized (mUsersLock) { + return mUserIdsIncludingPreCreated; + } + } + @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void readUserListLP() { if (!mUserListFile.exists()) { @@ -2873,6 +2897,9 @@ public class UserManagerService extends IUserManager.Stub { if (userInfo.preCreated) { serializer.attribute(null, ATTR_PRE_CREATED, "true"); } + if (userInfo.convertedFromPreCreated) { + serializer.attribute(null, ATTR_CONVERTED_FROM_PRE_CREATED, "true"); + } if (userInfo.guestToRemove) { serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); } @@ -3030,6 +3057,7 @@ public class UserManagerService extends IUserManager.Stub { int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID; boolean partial = false; boolean preCreated = false; + boolean converted = false; boolean guestToRemove = false; boolean persistSeedData = false; String seedAccountName = null; @@ -3081,6 +3109,10 @@ public class UserManagerService extends IUserManager.Stub { if ("true".equals(valueString)) { preCreated = true; } + valueString = parser.getAttributeValue(null, ATTR_CONVERTED_FROM_PRE_CREATED); + if ("true".equals(valueString)) { + converted = true; + } valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); if ("true".equals(valueString)) { guestToRemove = true; @@ -3138,6 +3170,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint; userInfo.partial = partial; userInfo.preCreated = preCreated; + userInfo.convertedFromPreCreated = converted; userInfo.guestToRemove = guestToRemove; userInfo.profileGroupId = profileGroupId; userInfo.profileBadge = profileBadge; @@ -3584,6 +3617,7 @@ public class UserManagerService extends IUserManager.Stub { preCreatedUser.name = name; preCreatedUser.flags = newFlags; preCreatedUser.preCreated = false; + preCreatedUser.convertedFromPreCreated = true; preCreatedUser.creationTime = getCreationTime(); synchronized (mPackagesLock) { @@ -4327,23 +4361,43 @@ public class UserManagerService extends IUserManager.Stub { */ private void updateUserIds() { int num = 0; + int numIncludingPreCreated = 0; synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { - UserInfo userInfo = mUsers.valueAt(i).info; - if (!userInfo.partial && !userInfo.preCreated) { - num++; + final UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial) { + numIncludingPreCreated++; + if (!userInfo.preCreated) { + num++; + } } } + if (DBG) { + Slog.d(LOG_TAG, "updateUserIds(): numberUsers= " + num + + " includingPreCreated=" + numIncludingPreCreated); + } final int[] newUsers = new int[num]; + final int[] newUsersIncludingPreCreated = new int[numIncludingPreCreated]; + int n = 0; + int nIncludingPreCreated = 0; for (int i = 0; i < userSize; i++) { - UserInfo userInfo = mUsers.valueAt(i).info; - if (!userInfo.partial && !userInfo.preCreated) { - newUsers[n++] = mUsers.keyAt(i); + final UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial) { + final int userId = mUsers.keyAt(i); + newUsersIncludingPreCreated[nIncludingPreCreated++] = userId; + if (!userInfo.preCreated) { + newUsers[n++] = userId; + } } } mUserIds = newUsers; + mUserIdsIncludingPreCreated = newUsersIncludingPreCreated; + if (DBG) { + Slog.d(LOG_TAG, "updateUserIds(): userIds= " + Arrays.toString(mUserIds) + + " includingPreCreated=" + Arrays.toString(mUserIdsIncludingPreCreated)); + } } } @@ -4626,6 +4680,7 @@ public class UserManagerService extends IUserManager.Stub { running ? " (running)" : "", user.partial ? " (partial)" : "", user.preCreated ? " (pre-created)" : "", + user.convertedFromPreCreated ? " (converted)" : "", current ? " (current)" : ""); } else { // NOTE: the standard "list users" command is used by integration tests and @@ -4710,6 +4765,9 @@ public class UserManagerService extends IUserManager.Stub { if (userInfo.preCreated) { pw.print(" <pre-created>"); } + if (userInfo.convertedFromPreCreated) { + pw.print(" <converted>"); + } pw.println(); pw.print(" Type: "); pw.println(userInfo.userType); pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" ("); @@ -4791,6 +4849,13 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mUserStates) { pw.println(" Started users state: " + mUserStates); } + synchronized (mUsersLock) { + pw.print(" Cached user IDs: "); + pw.println(Arrays.toString(mUserIds)); + pw.print(" Cached user IDs (including pre-created): "); + pw.println(Arrays.toString(mUserIdsIncludingPreCreated)); + } + } // synchronized (mPackagesLock) // Dump some capabilities @@ -5052,8 +5117,14 @@ public class UserManagerService extends IUserManager.Stub { @Override public @NonNull List<UserInfo> getUsers(boolean excludeDying) { - return UserManagerService.this.getUsersInternal(/*excludePartial= */ true, - excludeDying, /* excludePreCreated= */ true); + return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); + } + + @Override + public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + boolean excludePreCreated) { + return UserManagerService.this.getUsersInternal(excludePartial, excludeDying, + excludePreCreated); } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a4e8b1c01620..66d8b5974261 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -83,7 +83,6 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -121,7 +120,6 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -2605,13 +2603,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int N = pkg.getRequestedPermissions().size(); for (int i = 0; i < N; i++) { final String permName = pkg.getRequestedPermissions().get(i); + final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")"; final BasePermission bp = mSettings.getPermissionLocked(permName); final boolean appSupportsRuntimePermissions = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; String upgradedActivityRecognitionPermission = null; - if (DEBUG_INSTALL && bp != null) { - Log.i(TAG, "Package " + pkg.getPackageName() + Log.i(TAG, "Package " + friendlyName + " checking " + permName + ": " + bp); } @@ -2620,7 +2618,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { pkg.getPackageName())) { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Unknown permission " + permName - + " in package " + pkg.getPackageName()); + + " in package " + friendlyName); } } continue; @@ -2636,7 +2634,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { newImplicitPermissions.add(permName); if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName()); + Slog.i(TAG, permName + " is newly added for " + friendlyName); } } else { // Special case for Activity Recognition permission. Even if AR permission @@ -2658,8 +2656,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { newImplicitPermissions.add(permName); if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " - + pkg.getPackageName()); + Slog.i(TAG, permName + " is newly added for " + friendlyName); } break; } @@ -2673,7 +2670,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { // if (DEBUG_PERMISSIONS) { // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() -// + " for package " + pkg.getPackageName()); +// + " for package " + friendlyName); // } // continue; // } @@ -2681,7 +2678,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { if (DEBUG_PERMISSIONS) { Log.i(TAG, "Denying runtime-only permission " + bp.getName() - + " for package " + pkg.getPackageName()); + + " for package " + friendlyName); } continue; } @@ -2718,7 +2715,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Considering granting permission " + perm + " to package " - + pkg.getPackageName()); + + friendlyName); } if (grant != GRANT_DENIED) { @@ -3007,7 +3004,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { || packageOfInterest.equals(pkg.getPackageName())) { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() + + " to package " + friendlyName + " because it was previously installed without"); } } @@ -3022,7 +3019,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { changedInstallPermission = true; if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Un-granting permission " + perm - + " from package " + pkg.getPackageName() + + " from package " + friendlyName + " (protectionLevel=" + bp.getProtectionLevel() + " flags=0x" + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) @@ -3035,7 +3032,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { && (packageOfInterest == null || packageOfInterest.equals(pkg.getPackageName()))) { Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() + + " to package " + friendlyName + " (protectionLevel=" + bp.getProtectionLevel() + " flags=0x" + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) @@ -3078,17 +3075,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @return user ids for created users and pre-created users */ private int[] getAllUserIds() { - final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); - t.traceBegin("getAllUserIds"); - List<UserInfo> users = UserManagerService.getInstance().getUsers( - /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false); - int size = users.size(); - final int[] userIds = new int[size]; - for (int i = 0; i < size; i++) { - userIds[i] = users.get(i).id; - } - t.traceEnd(); - return userIds; + return UserManagerService.getInstance().getUserIdsIncludingPreCreated(); } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 3faf3be564c7..3c42e930389b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -69,6 +69,7 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -148,6 +149,7 @@ import android.os.UEventObserver; import android.os.UserHandle; import android.os.VibrationEffect; import android.os.Vibrator; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; @@ -1379,12 +1381,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private long getScreenshotChordLongPressDelay() { + long delayMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY, + ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout()); if (mKeyguardDelegate.isShowing()) { // Double the time it takes to take a screenshot from the keyguard - return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * - ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout()); + return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs); } - return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout(); + return delayMs; } private long getRingerToggleChordDelay() { diff --git a/services/core/java/com/android/server/telecom/InternalServiceRepository.java b/services/core/java/com/android/server/telecom/InternalServiceRepository.java new file mode 100644 index 000000000000..76ea5c788bd7 --- /dev/null +++ b/services/core/java/com/android/server/telecom/InternalServiceRepository.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.telecom; + +import android.content.Context; +import android.os.Binder; +import android.os.Process; + +import com.android.internal.telecom.IDeviceIdleControllerAdapter; +import com.android.internal.telecom.IInternalServiceRetriever; +import com.android.server.DeviceIdleInternal; + +/** + * The Telecom APK can not access services stored in LocalService directly and since it is in the + * SYSTEM process, it also can not use the *Manager interfaces + * (see {@link Context#enforceCallingPermission(String, String)}). Instead, we must wrap these local + * services in binder interfaces to allow Telecom access. + */ +public class InternalServiceRepository extends IInternalServiceRetriever.Stub { + + private final IDeviceIdleControllerAdapter.Stub mDeviceIdleControllerAdapter = + new IDeviceIdleControllerAdapter.Stub() { + @Override + public void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle, + String reason) { + mDeviceIdleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName, + duration, userHandle, true /*sync*/, reason); + } + }; + + private final DeviceIdleInternal mDeviceIdleController; + + public InternalServiceRepository(DeviceIdleInternal deviceIdleController) { + mDeviceIdleController = deviceIdleController; + } + + @Override + public IDeviceIdleControllerAdapter getDeviceIdleController() { + ensureSystemProcess(); + return mDeviceIdleControllerAdapter; + } + + private void ensureSystemProcess() { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + // Correctness check - this should never happen. + throw new SecurityException("SYSTEM ONLY API."); + } + } +} diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index a853529f49e4..52ad893a9ace 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -35,7 +35,10 @@ import android.util.IntArray; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.telecom.ITelecomLoader; +import com.android.internal.telecom.ITelecomService; import com.android.internal.telephony.SmsApplication; +import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerService; @@ -53,16 +56,13 @@ public class TelecomLoaderService extends SystemService { @Override public void onServiceConnected(ComponentName name, IBinder service) { // Normally, we would listen for death here, but since telecom runs in the same process - // as this loader (process="system") thats redundant here. + // as this loader (process="system") that's redundant here. try { - service.linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - connectToTelecom(); - } - }, 0); + ITelecomLoader telecomLoader = ITelecomLoader.Stub.asInterface(service); + ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo); + SmsApplication.getDefaultMmsApplication(mContext, false); - ServiceManager.addService(Context.TELECOM_SERVICE, service); + ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder()); synchronized (mLock) { final PermissionManagerServiceInternal permissionManager = @@ -114,6 +114,8 @@ public class TelecomLoaderService extends SystemService { @GuardedBy("mLock") private TelecomServiceConnection mServiceConnection; + private InternalServiceRepository mServiceRepo; + public TelecomLoaderService(Context context) { super(context); mContext = context; @@ -129,6 +131,8 @@ public class TelecomLoaderService extends SystemService { if (phase == PHASE_ACTIVITY_MANAGER_READY) { registerDefaultAppNotifier(); registerCarrierConfigChangedReceiver(); + // core services will have already been loaded. + setupServiceRepository(); connectToTelecom(); } } @@ -154,6 +158,11 @@ public class TelecomLoaderService extends SystemService { } } + private void setupServiceRepository() { + DeviceIdleInternal deviceIdleInternal = getLocalService(DeviceIdleInternal.class); + mServiceRepo = new InternalServiceRepository(deviceIdleInternal); + } + private void registerDefaultAppProviders() { final PermissionManagerServiceInternal permissionManager = diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 2394bafc09de..26103d5d6c8c 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -123,6 +123,8 @@ public class TrustManagerService extends SystemService { private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser"; private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000; + private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android"; + private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>(); private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>(); private final Receiver mReceiver = new Receiver(); @@ -808,8 +810,8 @@ public class TrustManagerService extends SystemService { TypedArray sa = res .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent); cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity); - canUnlockProfile = sa.getBoolean( - com.android.internal.R.styleable.TrustAgent_unlockProfile, false); + canUnlockProfile = attrs.getAttributeBooleanValue( + PRIV_NAMESPACE, "unlockProfile", false); sa.recycle(); } catch (PackageManager.NameNotFoundException e) { caughtException = e; diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java index 3f2b5c231dca..a713e5b13667 100644 --- a/services/core/java/com/android/server/vr/Vr2dDisplay.java +++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java @@ -295,6 +295,7 @@ class Vr2dDisplay { flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5339d86a7b78..1a13d0fc64c4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -70,7 +70,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS; import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY; @@ -1680,7 +1680,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (options != null) { final boolean useLockTask = options.getLockTaskMode(); if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) { - lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; + lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; } } return lockTaskLaunchMode; diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index cd508d0e1860..6d452c39eccc 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1700,8 +1700,9 @@ class ActivityStack extends Task { // If the most recent activity was noHistory but was only stopped rather // than stopped+finished because the device went to sleep, we need to make // sure to finish it as we're making a new activity topmost. - if (shouldSleepActivities() && mLastNoHistoryActivity != null && - !mLastNoHistoryActivity.finishing) { + if (shouldSleepActivities() && mLastNoHistoryActivity != null + && !mLastNoHistoryActivity.finishing + && mLastNoHistoryActivity != next) { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + mLastNoHistoryActivity + " on new resume"); mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */); @@ -1990,7 +1991,7 @@ class ActivityStack extends Task { return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea()); } - void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, + void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { Task rTask = r.getTask(); final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront(); @@ -3336,7 +3337,11 @@ class ActivityStack extends Task { // Do not sleep activities in this stack if we're marked as focused and the keyguard // is in the process of going away. if (isFocusedStackOnDisplay() - && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) { + && mStackSupervisor.getKeyguardController().isKeyguardGoingAway() + // Avoid resuming activities on secondary displays since we don't want bubble + // activities to be resumed while bubble is still collapsed. + // TODO(b/113840485): Having keyguard going away state for secondary displays. + && display.isDefaultDisplay) { return false; } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 6bfcf0c75b83..7e6b7cd05762 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -75,9 +75,9 @@ import static com.android.server.wm.RootWindowContainer.TAG_STATES; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -732,6 +732,11 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final ActivityStack stack = task.getStack(); beginDeferResume(); + // The LaunchActivityItem also contains process configuration, so the configuration change + // from WindowProcessController#setProcess can be deferred. The major reason is that if + // the activity has FixedRotationAdjustments, it needs to be applied with configuration. + // In general, this reduces a binder transaction if process configuration is changed. + proc.pauseConfigurationDispatch(); try { r.startFreezingScreenLocked(proc, 0); @@ -788,7 +793,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final LockTaskController lockTaskController = mService.getLockTaskController(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV - || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED + || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED && lockTaskController.getLockTaskModeState() == LOCK_TASK_MODE_LOCKED)) { lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */); @@ -826,9 +831,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Because we could be starting an Activity in the system process this may not go // across a Binder interface which would create a new Configuration. Consequently // we have to always create a new Configuration here. - + final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity(); final MergedConfiguration mergedConfiguration = new MergedConfiguration( - proc.getConfiguration(), r.getMergedOverrideConfiguration()); + procConfig, r.getMergedOverrideConfiguration()); r.setLastReportedConfiguration(mergedConfiguration); logIfTransactionTooLarge(r.intent, r.getSavedState()); @@ -862,6 +867,11 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); + if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) { + // If the seq is increased, there should be something changed (e.g. registered + // activity configuration). + proc.setLastReportedConfiguration(procConfig); + } if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mHasHeavyWeightFeature) { // This may be a heavy-weight process! Note that the package manager will ensure @@ -896,6 +906,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } finally { endDeferResume(); + proc.resumeConfigurationDispatch(); } r.launchFailed = false; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index b869eb56f536..7af237b80cfa 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1481,9 +1481,10 @@ class ActivityStarter { // anyone interested in this piece of information. final ActivityStack homeStack = targetTask.getDisplayArea().getRootHomeTask(); final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null); + final ActivityRecord top = targetTask.getTopNonFinishingActivity(); + final boolean visible = top != null && top.isVisible(); mService.getTaskChangeNotificationController().notifyActivityRestartAttempt( - targetTask.getTaskInfo(), homeTaskVisible, clearedTask, - targetTask.getTopNonFinishingActivity().isVisible()); + targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible); } } @@ -1697,8 +1698,9 @@ class ActivityStarter { mRootWindowContainer.sendPowerHintForLaunchStartIfNeeded( false /* forceSend */, mStartActivity); - mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(), - newTask, mKeepCurTransition, mOptions); + mTargetStack.startActivityLocked(mStartActivity, + topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask, + mKeepCurTransition, mOptions); if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index a903bcd3d728..d4dd35f53cf0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -520,8 +520,8 @@ public abstract class ActivityTaskManagerInternal { public abstract void onActiveUidsCleared(); public abstract void onUidProcStateChanged(int uid, int procState); - public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag); - public abstract void onUidRemovedFromPendingTempWhitelist(int uid); + public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag); + public abstract void onUidRemovedFromPendingTempAllowlist(int uid); /** Handle app crash event in {@link android.app.IActivityController} if there is one. */ public abstract boolean handleAppCrashInActivityController(String processName, int pid, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 52d4b6fc78df..0542ef9b09a4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -382,7 +382,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private AppOpsManager mAppOpsManager; /** All active uids in the system. */ private final MirrorActiveUids mActiveUids = new MirrorActiveUids(); - private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); + private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); /** All processes we currently have running mapped by pid and uid */ @@ -1099,7 +1099,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int startActivityIntentSender(IApplicationThread caller, IIntentSender target, - IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, + IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) { enforceNotIsolatedCaller("startActivityIntentSender"); // Refuse possible leaked file descriptors @@ -1122,7 +1122,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesAllowedTime = 0; } } - return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null, + return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions); } @@ -3025,7 +3025,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // system or a specific app. // * System-initiated requests will only start the pinned mode (screen pinning) // * App-initiated requests - // - will put the device in fully locked mode (LockTask), if the app is whitelisted + // - will put the device in fully locked mode (LockTask), if the app is allowlisted // - will start the pinned mode, otherwise final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); @@ -3073,7 +3073,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "updateLockTaskPackages()"); } synchronized (mGlobalLock) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":" + Arrays.toString(packages)); getLockTaskController().updateLockTaskPackages(userId, packages); } @@ -5962,11 +5962,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** - * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on - * the whitelist + * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on + * the allowlist */ - String getPendingTempWhitelistTagForUidLocked(int uid) { - return mPendingTempWhitelist.get(uid); + String getPendingTempAllowlistTagForUidLocked(int uid) { + return mPendingTempAllowlist.get(uid); } void logAppTooSlow(WindowProcessController app, long startTime, String msg) { @@ -7295,16 +7295,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onUidAddedToPendingTempWhitelist(int uid, String tag) { + public void onUidAddedToPendingTempAllowlist(int uid, String tag) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.put(uid, tag); + mPendingTempAllowlist.put(uid, tag); } } @Override - public void onUidRemovedFromPendingTempWhitelist(int uid) { + public void onUidRemovedFromPendingTempAllowlist(int uid) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.remove(uid); + mPendingTempAllowlist.remove(uid); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0363ea0e7512..553aad1a43cd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -43,14 +43,14 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; +import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; -import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; -import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; @@ -3554,7 +3554,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return false; } return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this) - || mWmService.mForceDesktopModeOnExternalDisplays; + || forceDesktopMode(); + } + + boolean forceDesktopMode() { + return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate(); } private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { @@ -4786,7 +4790,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo boolean supportsSystemDecorations() { return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this) || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 - || mWmService.mForceDesktopModeOnExternalDisplays) + || forceDesktopMode()) // VR virtual display will be used to run and render 2D app within a VR experience. && mDisplayId != mWmService.mVr2dDisplayId // Do not show system decorations on untrusted virtual display. @@ -4983,7 +4987,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } // Apply restriction if necessary. - if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) { + if (needsGestureExclusionRestrictions(w, false /* ignoreRequest */)) { // Processes the region along the left edge. remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, outExclusion, leftEdge, @@ -5000,7 +5004,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo outExclusion.op(middle, Op.UNION); middle.recycle(); } else { - boolean loggable = needsGestureExclusionRestrictions(w, 0 /* lastSysUiVis */); + boolean loggable = needsGestureExclusionRestrictions(w, true /* ignoreRequest */); if (loggable) { addToGlobalAndConsumeLimit(local, outExclusion, leftEdge, Integer.MAX_VALUE, w, EXCLUSION_LEFT); @@ -5022,17 +5026,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** - * @return Whether gesture exclusion area should be restricted from the window depending on the - * current SystemUI visibility flags. + * Returns whether gesture exclusion area should be restricted from the window depending on the + * window/activity types and the requested navigation bar visibility and the behavior. + * + * @param win The target window. + * @param ignoreRequest If this is {@code true}, only the window/activity types are considered. + * @return {@code true} if the gesture exclusion restrictions are needed. */ - private static boolean needsGestureExclusionRestrictions(WindowState win, int sysUiVisibility) { + private static boolean needsGestureExclusionRestrictions(WindowState win, + boolean ignoreRequest) { final int type = win.mAttrs.type; - final int stickyHideNavFlags = - SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY; final boolean stickyHideNav = - (sysUiVisibility & stickyHideNavFlags) == stickyHideNavFlags; - return !stickyHideNav && type != TYPE_INPUT_METHOD && type != TYPE_NOTIFICATION_SHADE - && win.getActivityType() != ACTIVITY_TYPE_HOME; + !win.getRequestedInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + && win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD + && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME; } /** @@ -5048,7 +5056,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo && type != TYPE_APPLICATION_STARTING && type != TYPE_NAVIGATION_BAR && (attrs.flags & FLAG_NOT_TOUCHABLE) == 0 - && needsGestureExclusionRestrictions(win, 0 /* sysUiVisibility */) + && needsGestureExclusionRestrictions(win, true /* ignoreRequest */) && win.getDisplayContent().mDisplayPolicy.hasSideGestures(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5c5ec358d004..c691a0e220ed 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,6 +29,9 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -71,6 +74,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; @@ -246,7 +250,8 @@ public class DisplayPolicy { | View.STATUS_BAR_TRANSPARENT | View.NAVIGATION_BAR_TRANSPARENT; - private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR}; + private static final int[] SHOW_TYPES_FOR_SWIPE = + {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR}; private final WindowManagerService mService; @@ -332,6 +337,16 @@ public class DisplayPolicy { private WindowState mNavigationBarAlt = null; @WindowManagerPolicy.AltBarPosition private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN; + // Alternative climate bar for when flexible insets mapping is used to place a climate bar on + // the screen. + private WindowState mClimateBarAlt = null; + @WindowManagerPolicy.AltBarPosition + private int mClimateBarAltPosition = ALT_BAR_UNKNOWN; + // Alternative extra nav bar for when flexible insets mapping is used to place an extra nav bar + // on the screen. + private WindowState mExtraNavBarAlt = null; + @WindowManagerPolicy.AltBarPosition + private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN; /** See {@link #getNavigationBarFrameHeight} */ private int[] mNavigationBarFrameHeightForRotationDefault = new int[4]; @@ -668,6 +683,12 @@ public class DisplayPolicy { if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) { requestTransientBars(mNavigationBarAlt); } + if (mClimateBarAlt != null && mClimateBarAltPosition == pos) { + requestTransientBars(mClimateBarAlt); + } + if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) { + requestTransientBars(mExtraNavBarAlt); + } } void systemReady() { @@ -939,6 +960,12 @@ public class DisplayPolicy { if (mNavigationBarAlt == win) { mNavigationBarAltPosition = getAltBarPosition(attrs); } + if (mClimateBarAlt == win) { + mClimateBarAltPosition = getAltBarPosition(attrs); + } + if (mExtraNavBarAlt == win) { + mExtraNavBarAltPosition = getAltBarPosition(attrs); + } } /** @@ -1046,6 +1073,16 @@ public class DisplayPolicy { return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } break; + case ITYPE_CLIMATE_BAR: + if (mClimateBarAlt != null && mClimateBarAlt.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + break; + case ITYPE_EXTRA_NAVIGATION_BAR: + if (mExtraNavBarAlt != null && mExtraNavBarAlt.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + break; } } } @@ -1151,6 +1188,14 @@ public class DisplayPolicy { mNavigationBarController.setWindow(mNavigationBarAlt); mNavigationBarAltPosition = getAltBarPosition(attrs); break; + case ITYPE_CLIMATE_BAR: + mClimateBarAlt = win; + mClimateBarAltPosition = getAltBarPosition(attrs); + break; + case ITYPE_EXTRA_NAVIGATION_BAR: + mExtraNavBarAlt = win; + mExtraNavBarAltPosition = getAltBarPosition(attrs); + break; } mDisplayContent.setInsetProvider(insetsType, win, null); } @@ -1199,6 +1244,8 @@ public class DisplayPolicy { switch (insetsType) { case ITYPE_NAVIGATION_BAR: case ITYPE_STATUS_BAR: + case ITYPE_CLIMATE_BAR: + case ITYPE_EXTRA_NAVIGATION_BAR: case ITYPE_CAPTION_BAR: if (++count > 1) { throw new IllegalArgumentException( @@ -1230,6 +1277,12 @@ public class DisplayPolicy { if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(null); } + } else if (mClimateBarAlt == win) { + mClimateBarAlt = null; + mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null); + } else if (mExtraNavBarAlt == win) { + mExtraNavBarAlt = null; + mDisplayContent.setInsetProvider(ITYPE_EXTRA_NAVIGATION_BAR, null, null); } if (mLastFocusedWindow == win) { mLastFocusedWindow = null; @@ -1318,7 +1371,8 @@ public class DisplayPolicy { return R.anim.dock_left_enter; } } - } else if (win == mStatusBarAlt || win == mNavigationBarAlt) { + } else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt + || win == mExtraNavBarAlt) { if (win.getAttrs().windowAnimations != 0) { return ANIMATION_STYLEABLE; } @@ -1538,7 +1592,7 @@ public class DisplayPolicy { if (mInputConsumer == null) { return; } - showNavigationBar(); + showSystemBars(); // Any user activity always causes us to show the // navigation controls, if they had been hidden. // We also clear the low profile and only content @@ -1573,13 +1627,13 @@ public class DisplayPolicy { } } - private void showNavigationBar() { + private void showSystemBars() { final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() .peekSourceProvider(ITYPE_NAVIGATION_BAR); final InsetsControlTarget target = provider != null ? provider.getControlTarget() : null; if (target != null) { - target.showInsets(Type.navigationBars(), false /* fromIme */); + target.showInsets(Type.systemBars(), false /* fromIme */); } } } @@ -2191,6 +2245,13 @@ public class DisplayPolicy { df.set(left, top, dfu.right - right, dfu.bottom - bottom); if (attached == null) { pf.set(df); + if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { + final InsetsSource source = mDisplayContent.getInsetsPolicy() + .getInsetsForDispatch(win).peekSource(ITYPE_IME); + if (source != null) { + pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */)); + } + } vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING ? displayFrames.mCurrent : displayFrames.mDock); } else { @@ -3338,6 +3399,13 @@ public class DisplayPolicy { (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) + ? Type.statusBars() : 0) + | (mExtraNavBarAlt != null + && requestedState.getSourceOrDefaultVisibility( + ITYPE_EXTRA_NAVIGATION_BAR) + ? Type.navigationBars() : 0) + | (mClimateBarAlt != null + && requestedState.getSourceOrDefaultVisibility(ITYPE_CLIMATE_BAR) ? Type.statusBars() : 0); if (swipeTarget == mNavigationBar @@ -4037,6 +4105,16 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mNavigationBarAltPosition="); pw.println(mNavigationBarAltPosition); } + if (mClimateBarAlt != null) { + pw.print(prefix); pw.print("mClimateBarAlt="); pw.println(mClimateBarAlt); + pw.print(prefix); pw.print("mClimateBarAltPosition="); + pw.println(mClimateBarAltPosition); + } + if (mExtraNavBarAlt != null) { + pw.print(prefix); pw.print("mExtraNavBarAlt="); pw.println(mExtraNavBarAlt); + pw.print(prefix); pw.print("mExtraNavBarAltPosition="); + pw.println(mExtraNavBarAltPosition); + } if (mFocusedWindow != null) { pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 37ecee8e3db3..1284e009ee3f 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -330,10 +330,8 @@ public class DisplayRotation { // It's also not likely to rotate a TV screen. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); - final boolean forceDesktopMode = - mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay; mDefaultFixedToUserRotation = - (isCar || isTv || mService.mIsPc || forceDesktopMode) + (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode()) // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true // $ adb shell wm size reset diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 470a02e5bd44..34b403b18d20 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -248,7 +248,7 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } - private int getWindowingModeLocked(Entry entry, int displayId) { + private int getWindowingModeLocked(Entry entry, DisplayContent dc) { int windowingMode = entry != null ? entry.mWindowingMode : WindowConfiguration.WINDOWING_MODE_UNDEFINED; // This display used to be in freeform, but we don't support freeform anymore, so fall @@ -259,10 +259,8 @@ class DisplayWindowSettings { } // No record is present so use default windowing mode policy. if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays - && displayId != Display.DEFAULT_DISPLAY; windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement - && (mService.mIsPc || forceDesktopMode) + && (mService.mIsPc || dc.forceDesktopMode()) ? WindowConfiguration.WINDOWING_MODE_FREEFORM : WindowConfiguration.WINDOWING_MODE_FULLSCREEN; } @@ -272,7 +270,7 @@ class DisplayWindowSettings { int getWindowingModeLocked(DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final Entry entry = getEntry(displayInfo); - return getWindowingModeLocked(entry, dc.getDisplayId()); + return getWindowingModeLocked(entry, dc); } void setWindowingModeLocked(DisplayContent dc, int mode) { @@ -382,7 +380,7 @@ class DisplayWindowSettings { final Entry entry = getOrCreateEntry(displayInfo); // Setting windowing mode first, because it may override overscan values later. - dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId())); + dc.setWindowingMode(getWindowingModeLocked(entry, dc)); dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, entry.mUserRotation, entry.mFixedToUserRotation); diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index e7fbc334306e..5ab48e158c4d 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -138,8 +138,9 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { && dcTarget.getParentWindow() == mImeTargetFromIme && dcTarget.mSubLayer > mImeTargetFromIme.getWindow().mSubLayer) || mImeTargetFromIme == mDisplayContent.getImeFallback() + || mImeTargetFromIme == mDisplayContent.mInputMethodInputTarget || controlTarget == mImeTargetFromIme - && (mImeTargetFromIme.getWindow() == null + && (mImeTargetFromIme.getWindow() == null || !mImeTargetFromIme.getWindow().isClosing()); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 0216db471843..3663ee14ea99 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -219,6 +219,11 @@ final class InputMonitor { WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) { + if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); + } + if (mInputConsumers.containsKey(name)) { throw new IllegalStateException("Existing input consumer found with name: " + name + ", display: " + mDisplayId); @@ -248,6 +253,11 @@ final class InputMonitor { // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL; break; + case INPUT_CONSUMER_RECENTS_ANIMATION: + break; + default: + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); } addInputConsumer(name, consumer); } @@ -459,9 +469,6 @@ final class InputMonitor { mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); - if (mAddWallpaperInputConsumerHandle) { - mWallpaperInputConsumer.show(mInputTransaction, 0); - } if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 8e6a2656fce3..9fdfbd0a09da 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -94,7 +96,8 @@ class InsetsSourceProvider { new Point()); final int type = source.getType(); - if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR) { + if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR || type == ITYPE_CLIMATE_BAR + || type == ITYPE_EXTRA_NAVIGATION_BAR) { mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL; } else if (type == ITYPE_IME) { mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME; diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 77bd4a47a884..68405c40ce37 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -43,6 +45,7 @@ import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams.WindowType; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.protolog.common.ProtoLog; @@ -112,7 +115,7 @@ class InsetsStateController { } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { - final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type); + final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs); final WindowToken token = mDisplayContent.getWindowToken(attrs.token); final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; @@ -132,7 +135,9 @@ class InsetsStateController { return false; } - private static @InternalInsetsType int getInsetsTypeForWindowType(int type) { + private static @InternalInsetsType + int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) { + @WindowType int type = attrs.type; switch (type) { case TYPE_STATUS_BAR: return ITYPE_STATUS_BAR; @@ -140,9 +145,22 @@ class InsetsStateController { return ITYPE_NAVIGATION_BAR; case TYPE_INPUT_METHOD: return ITYPE_IME; - default: - return ITYPE_INVALID; } + + // If not one of the types above, check whether an internal inset mapping is specified. + if (attrs.providesInsetsTypes != null) { + for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { + switch (insetsType) { + case ITYPE_STATUS_BAR: + case ITYPE_NAVIGATION_BAR: + case ITYPE_CLIMATE_BAR: + case ITYPE_EXTRA_NAVIGATION_BAR: + return insetsType; + } + } + } + + return ITYPE_INVALID; } /** @see #getInsetsForDispatch */ @@ -155,14 +173,15 @@ class InsetsStateController { state.removeSource(type); // Navigation bar doesn't get influenced by anything else - if (type == ITYPE_NAVIGATION_BAR) { + if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) { state.removeSource(ITYPE_IME); state.removeSource(ITYPE_STATUS_BAR); + state.removeSource(ITYPE_CLIMATE_BAR); state.removeSource(ITYPE_CAPTION_BAR); } // Status bar doesn't get influenced by caption bar - if (type == ITYPE_STATUS_BAR) { + if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) { state.removeSource(ITYPE_CAPTION_BAR); } @@ -280,6 +299,7 @@ class InsetsStateController { } if (changed) { notifyInsetsChanged(); + mDisplayContent.updateSystemGestureExclusion(); mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw(); } } @@ -331,8 +351,12 @@ class InsetsStateController { @Nullable InsetsControlTarget fakeNavControlling) { onControlChanged(ITYPE_STATUS_BAR, statusControlling); onControlChanged(ITYPE_NAVIGATION_BAR, navControlling); + onControlChanged(ITYPE_CLIMATE_BAR, statusControlling); + onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling); onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling); onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling); + onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling); + onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling); notifyPendingInsetsControlChanged(); } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 892ee717e21f..c36dede013f4 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -33,11 +33,11 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTAS import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -264,12 +264,12 @@ public class LockTaskController { } /** - * @return whether the requested task is allowed to be locked (either whitelisted, or declares + * @return whether the requested task is allowed to be locked (either allowlisted, or declares * lockTaskMode="always" in the manifest). */ - boolean isTaskWhitelisted(Task task) { + boolean isTaskAllowlisted(Task task) { switch(task.mLockTaskAuth) { - case LOCK_TASK_AUTH_WHITELISTED: + case LOCK_TASK_AUTH_ALLOWLISTED: case LOCK_TASK_AUTH_LAUNCHABLE: case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return true; @@ -311,7 +311,7 @@ public class LockTaskController { private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) { // TODO: Double check what's going on here. If the task is already in lock task mode, it's - // likely whitelisted, so will return false below. + // likely allowlisted, so will return false below. if (isTaskLocked(task) && !isNewClearTask) { // If the task is already at the top and won't be cleared, then allow the operation return false; @@ -327,7 +327,7 @@ public class LockTaskController { return false; } - return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty()); + return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty()); } private boolean isRecentsAllowed(int userId) { @@ -356,7 +356,7 @@ public class LockTaskController { return false; default: } - return isPackageWhitelisted(userId, packageName); + return isPackageAllowlisted(userId, packageName); } private boolean isEmergencyCallTask(Task task) { @@ -556,7 +556,7 @@ public class LockTaskController { if (!isSystemCaller) { task.mLockTaskUid = callingUid; if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) { - // startLockTask() called by app, but app is not part of lock task whitelist. Show + // startLockTask() called by app, but app is not part of lock task allowlist. Show // app pinning request. We will come back here with isSystemCaller true. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user"); StatusBarManagerInternal statusBarManager = LocalServices.getService( @@ -649,8 +649,8 @@ public class LockTaskController { /** * Update packages that are allowed to be launched in lock task mode. - * @param userId Which user this whitelist is associated with - * @param packages The whitelist of packages allowed in lock task mode + * @param userId Which user this allowlist is associated with + * @param packages The allowlist of packages allowed in lock task mode * @see #mLockTaskPackages */ void updateLockTaskPackages(int userId, String[] packages) { @@ -659,19 +659,19 @@ public class LockTaskController { boolean taskChanged = false; for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task lockedTask = mLockTaskModeTasks.get(taskNdx); - final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean wasAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; lockedTask.setLockTaskAuth(); - final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean isAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || lockedTask.mUserId != userId - || !wasWhitelisted || isWhitelisted) { + || !wasAllowlisted || isAllowlisted) { continue; } - // Terminate locked tasks that have recently lost whitelist authorization. + // Terminate locked tasks that have recently lost allowlist authorization. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); removeLockedTask(lockedTask); @@ -697,17 +697,17 @@ public class LockTaskController { } } - boolean isPackageWhitelisted(int userId, String pkg) { + boolean isPackageAllowlisted(int userId, String pkg) { if (pkg == null) { return false; } - String[] whitelist; - whitelist = mLockTaskPackages.get(userId); - if (whitelist == null) { + String[] allowlist; + allowlist = mLockTaskPackages.get(userId); + if (allowlist == null) { return false; } - for (String whitelistedPkg : whitelist) { - if (pkg.equals(whitelistedPkg)) { + for (String allowlistedPkg : allowlist) { + if (pkg.equals(allowlistedPkg)) { return true; } } diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java index 0f92bc83a666..61b6e0b25961 100644 --- a/services/core/java/com/android/server/wm/PolicyControl.java +++ b/services/core/java/com/android/server/wm/PolicyControl.java @@ -196,40 +196,40 @@ class PolicyControl { private static final String ALL = "*"; private static final String APPS = "apps"; - private final ArraySet<String> mWhitelist; - private final ArraySet<String> mBlacklist; + private final ArraySet<String> mAllowlist; + private final ArraySet<String> mDenylist; - private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { - mWhitelist = whitelist; - mBlacklist = blacklist; + private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) { + mAllowlist = allowlist; + mDenylist = denylist; } boolean matches(LayoutParams attrs) { if (attrs == null) return false; boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; - if (isApp && mBlacklist.contains(APPS)) return false; - if (onBlacklist(attrs.packageName)) return false; - if (isApp && mWhitelist.contains(APPS)) return true; - return onWhitelist(attrs.packageName); + if (isApp && mDenylist.contains(APPS)) return false; + if (onDenylist(attrs.packageName)) return false; + if (isApp && mAllowlist.contains(APPS)) return true; + return onAllowlist(attrs.packageName); } boolean matches(String packageName) { - return !onBlacklist(packageName) && onWhitelist(packageName); + return !onDenylist(packageName) && onAllowlist(packageName); } - private boolean onBlacklist(String packageName) { - return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + private boolean onDenylist(String packageName) { + return mDenylist.contains(packageName) || mDenylist.contains(ALL); } - private boolean onWhitelist(String packageName) { - return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + private boolean onAllowlist(String packageName) { + return mAllowlist.contains(ALL) || mAllowlist.contains(packageName); } void dump(PrintWriter pw) { pw.print("Filter["); - dump("whitelist", mWhitelist, pw); pw.print(','); - dump("blacklist", mBlacklist, pw); pw.print(']'); + dump("allowlist", mAllowlist, pw); pw.print(','); + dump("denylist", mDenylist, pw); pw.print(']'); } private void dump(String name, ArraySet<String> set, PrintWriter pw) { @@ -253,18 +253,18 @@ class PolicyControl { // e.g. "com.package1", or "apps, com.android.keyguard" or "*" static Filter parse(String value) { if (value == null) return null; - ArraySet<String> whitelist = new ArraySet<String>(); - ArraySet<String> blacklist = new ArraySet<String>(); + ArraySet<String> allowlist = new ArraySet<String>(); + ArraySet<String> denylist = new ArraySet<String>(); for (String token : value.split(",")) { token = token.trim(); if (token.startsWith("-") && token.length() > 1) { token = token.substring(1); - blacklist.add(token); + denylist.add(token); } else { - whitelist.add(token); + allowlist.add(token); } } - return new Filter(whitelist, blacklist); + return new Filter(allowlist, denylist); } } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 851b533a550d..3fe75a4ab49e 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -655,7 +655,8 @@ class RecentTasks { } for (int i = mTasks.size() - 1; i >= 0; --i) { final Task task = mTasks.get(i); - if (task.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(task)) { + if (task.mUserId == userId + && !mService.getLockTaskController().isTaskAllowlisted(task)) { remove(task); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 049e46fb6cef..72cd32f8d057 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -48,6 +48,7 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; +import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList; @@ -1931,24 +1932,29 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } boolean attachApplication(WindowProcessController app) throws RemoteException { - final String processName = app.mName; boolean didSomething = false; for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - final ActivityStack stack = display.getFocusedStack(); - if (stack == null) { - continue; - } - mTmpRemoteException = null; mTmpBoolean = false; // Set to true if an activity was started. - final PooledFunction c = PooledLambda.obtainFunction( - RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this, - PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity()); - stack.forAllActivities(c); - c.recycle(); - if (mTmpRemoteException != null) { - throw mTmpRemoteException; + + final DisplayContent display = getChildAt(displayNdx); + for (int areaNdx = display.getTaskDisplayAreaCount() - 1; areaNdx >= 0; --areaNdx) { + final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(areaNdx); + for (int taskNdx = taskDisplayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) { + final ActivityStack rootTask = taskDisplayArea.getStackAt(taskNdx); + if (rootTask.getVisibility(null /*starting*/) == STACK_VISIBILITY_INVISIBLE) { + break; + } + final PooledFunction c = PooledLambda.obtainFunction( + RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this, + PooledLambda.__(ActivityRecord.class), app, + rootTask.topRunningActivity()); + rootTask.forAllActivities(c); + c.recycle(); + if (mTmpRemoteException != null) { + throw mTmpRemoteException; + } + } } didSomething |= mTmpBoolean; } @@ -1966,8 +1972,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } try { - if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/, - true /*checkConfig*/)) { + if (mStackSupervisor.realStartActivityLocked(r, app, + top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) { mTmpBoolean = true; } } catch (RemoteException e) { @@ -2305,10 +2311,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent display = getChildAt(displayNdx); - if (display.shouldSleep()) { - continue; - } - boolean resumedOnDisplay = false; for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); @@ -2590,7 +2592,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mDisplayAccessUIDs.clear(); for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent displayContent = getChildAt(displayNdx); - // Only bother calculating the whitelist for private displays + // Only bother calculating the allowlist for private displays if (displayContent.isPrivate()) { mDisplayAccessUIDs.append( displayContent.mDisplayId, displayContent.getPresentUIDs()); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index b71ecbb8a72d..ede6708d5f8f 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -233,10 +233,10 @@ public class SafeActivityOptions { Slog.w(TAG, msg); throw new SecurityException(msg); } - // Check if someone tries to launch an unwhitelisted activity into LockTask mode. + // Check if someone tries to launch an unallowlisted activity into LockTask mode. final boolean lockTaskMode = options.getLockTaskMode(); if (aInfo != null && lockTaskMode - && !supervisor.mService.getLockTaskController().isPackageWhitelisted( + && !supervisor.mService.getLockTaskController().isPackageAllowlisted( UserHandle.getUserId(callingUid), aInfo.packageName)) { final String msg = "Permission Denial: starting " + getIntentString(intent) + " from " + callerApp + " (pid=" + callingPid diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 954b7a026bf5..db3c74fc94af 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -40,7 +40,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -264,7 +264,7 @@ class Task extends WindowContainer<WindowContainer> { /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE = 2; /** Can enter lockTask without user approval. Can start over existing lockTask task. */ - final static int LOCK_TASK_AUTH_WHITELISTED = 3; + final static int LOCK_TASK_AUTH_ALLOWLISTED = 3; /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing * lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4; @@ -1377,7 +1377,7 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().addStackReferenceIfNeeded((ActivityStack) child); } - // Make sure the list of display UID whitelists is updated + // Make sure the list of display UID allowlists is updated // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); @@ -1605,7 +1605,7 @@ class Task extends WindowContainer<WindowContainer> { case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK"; case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE"; case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE"; - case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED"; + case LOCK_TASK_AUTH_ALLOWLISTED: return "LOCK_TASK_AUTH_ALLOWLISTED"; case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV"; default: return "unknown=" + mLockTaskAuth; } @@ -1625,8 +1625,8 @@ class Task extends WindowContainer<WindowContainer> { final LockTaskController lockTaskController = mAtmService.getLockTaskController(); switch (r.lockTaskLaunchMode) { case LOCK_TASK_LAUNCH_MODE_DEFAULT: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) - ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) + ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: @@ -1637,8 +1637,8 @@ class Task extends WindowContainer<WindowContainer> { mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV; break; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } @@ -3671,6 +3671,10 @@ class Task extends WindowContainer<WindowContainer> { return STACK_VISIBILITY_INVISIBLE; } + if (isTopActivityLaunchedBehind()) { + return STACK_VISIBILITY_VISIBLE; + } + boolean gotSplitScreenStack = false; boolean gotOpaqueSplitScreenPrimary = false; boolean gotOpaqueSplitScreenSecondary = false; @@ -3788,6 +3792,14 @@ class Task extends WindowContainer<WindowContainer> { : STACK_VISIBILITY_VISIBLE; } + private boolean isTopActivityLaunchedBehind() { + final ActivityRecord top = topRunningActivity(); + if (top != null && top.mLaunchTaskBehind) { + return true; + } + return false; + } + ActivityRecord isInTask(ActivityRecord r) { if (r == null) { return null; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b7ac54f3470e..b7a2eb3c705d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.MANAGE_APP_TOKENS; @@ -1476,9 +1477,14 @@ public class WindowManagerService extends IWindowManager.Stub rootType, attrs.token, attrs.packageName)) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); - token = new WindowToken(this, binder, type, false, displayContent, - session.mCanAddInternalSystemWindow, isRoundedCornerOverlay); + if (hasParent) { + // Use existing parent window token for child windows. + token = parentWindow.mToken; + } else { + final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); + token = new WindowToken(this, binder, type, false, displayContent, + session.mCanAddInternalSystemWindow, isRoundedCornerOverlay); + } } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { activity = token.asActivityRecord(); @@ -5869,6 +5875,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void createInputConsumer(IBinder token, String name, int displayId, InputChannel inputChannel) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { @@ -5880,6 +5891,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean destroyInputConsumer(String name, int displayId) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { @@ -6171,7 +6187,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (inputMethodControlTarget != null) { pw.print(" inputMethodControlTarget in display# "); pw.print(displayId); - pw.print(' '); pw.println(inputMethodControlTarget.getWindow()); + pw.print(' '); pw.println(inputMethodControlTarget); } }); pw.print(" mInTouchMode="); pw.println(mInTouchMode); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index bd959aba5bb1..df49ac71334f 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -186,13 +186,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Last configuration that was reported to the process. private final Configuration mLastReportedConfiguration = new Configuration(); - // Configuration that is waiting to be dispatched to the process. - private Configuration mPendingConfiguration; + /** Whether the process configuration is waiting to be dispatched to the process. */ + private boolean mHasPendingConfigurationChange; // Registered display id as a listener to override config change private int mDisplayId; private ActivityRecord mConfigActivityRecord; // Whether the activity config override is allowed for this process. private volatile boolean mIsActivityConfigOverrideAllowed = true; + /** Non-zero to pause dispatching process configuration change. */ + private int mPauseConfigurationDispatchCount; + /** * Activities that hosts some UI drawn by the current process. The activities live * in another process. This is used to check if the process is currently showing anything @@ -1115,8 +1118,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio onMergedOverrideConfigurationChanged(Configuration.EMPTY); } - private void registerActivityConfigurationListener(ActivityRecord activityRecord) { - if (activityRecord == null || activityRecord.containsListener(this)) { + void registerActivityConfigurationListener(ActivityRecord activityRecord) { + if (activityRecord == null || activityRecord.containsListener(this) + // Check for the caller from outside of this class. + || !mIsActivityConfigOverrideAllowed) { return; } // A process can only register to one activityRecord to listen to the override configuration @@ -1168,25 +1173,25 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { - super.onRequestedOverrideConfigurationChanged( - sanitizeProcessConfiguration(overrideConfiguration)); + super.onRequestedOverrideConfigurationChanged(overrideConfiguration); } @Override public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) { - super.onRequestedOverrideConfigurationChanged( - sanitizeProcessConfiguration(mergedOverrideConfig)); + super.onRequestedOverrideConfigurationChanged(mergedOverrideConfig); } - private static Configuration sanitizeProcessConfiguration(Configuration config) { + @Override + void resolveOverrideConfiguration(Configuration newParentConfig) { + super.resolveOverrideConfiguration(newParentConfig); + final Configuration resolvedConfig = getResolvedOverrideConfiguration(); // Make sure that we don't accidentally override the activity type. - if (config.windowConfiguration.getActivityType() != ACTIVITY_TYPE_UNDEFINED) { - final Configuration sanitizedConfig = new Configuration(config); - sanitizedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); - return sanitizedConfig; - } - - return config; + resolvedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); + // Activity has an independent ActivityRecord#mConfigurationSeq. If this process registers + // activity configuration, its config seq shouldn't go backwards by activity configuration. + // Otherwise if other places send wpc.getConfiguration() to client, the configuration may + // be ignored due to the seq is older. + resolvedConfig.seq = newParentConfig.seq; } private void updateConfiguration() { @@ -1204,11 +1209,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (mListener.isCached()) { // This process is in a cached state. We will delay delivering the config change to the // process until the process is no longer cached. - if (mPendingConfiguration == null) { - mPendingConfiguration = new Configuration(config); - } else { - mPendingConfiguration.setTo(config); - } + mHasPendingConfigurationChange = true; return; } @@ -1216,6 +1217,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } private void dispatchConfigurationChange(Configuration config) { + if (mPauseConfigurationDispatchCount > 0) { + mHasPendingConfigurationChange = true; + return; + } + mHasPendingConfigurationChange = false; if (mThread == null) { if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. @@ -1242,7 +1248,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } - private void setLastReportedConfiguration(Configuration config) { + void setLastReportedConfiguration(Configuration config) { mLastReportedConfiguration.setTo(config); } @@ -1250,6 +1256,30 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mLastReportedConfiguration; } + void pauseConfigurationDispatch() { + mPauseConfigurationDispatchCount++; + } + + void resumeConfigurationDispatch() { + mPauseConfigurationDispatchCount--; + } + + /** + * This is called for sending {@link android.app.servertransaction.LaunchActivityItem}. + * The caller must call {@link #setLastReportedConfiguration} if the delivered configuration + * is newer. + */ + Configuration prepareConfigurationForLaunchingActivity() { + final Configuration config = getConfiguration(); + if (mHasPendingConfigurationChange) { + mHasPendingConfigurationChange = false; + // The global configuration may not change, so the client process may have the same + // config seq. This increment ensures that the client won't ignore the configuration. + config.seq = mAtm.increaseConfigurationSeqLocked(); + } + return config; + } + /** Returns the total time (in milliseconds) spent executing in both user and system code. */ public long getCpuTime() { return (mListener != null) ? mListener.getCpuTime() : 0; @@ -1341,10 +1371,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio public void onProcCachedStateChanged(boolean isCached) { if (!isCached) { synchronized (mAtm.mGlobalLockWithoutBoost) { - if (mPendingConfiguration != null) { - final Configuration config = mPendingConfiguration; - mPendingConfiguration = null; - dispatchConfigurationChange(config); + if (mHasPendingConfigurationChange) { + dispatchConfigurationChange(getConfiguration()); } } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 1716dcd5ee16..d86f6c998baa 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -172,6 +172,12 @@ class WindowToken extends WindowContainer<WindowState> { } } } + + /** The state may not only be used by self. Make sure to leave the influence by others. */ + void disassociate(WindowToken token) { + mAssociatedTokens.remove(token); + mRotatedContainers.remove(token); + } } private class DeathRecipient implements IBinder.DeathRecipient { @@ -531,7 +537,7 @@ class WindowToken extends WindowContainer<WindowState> { void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config) { if (mFixedRotationTransformState != null) { - cleanUpFixedRotationTransformState(true /* replacing */); + mFixedRotationTransformState.disassociate(this); } mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, new Configuration(config), mDisplayContent.getRotation()); @@ -539,8 +545,7 @@ class WindowToken extends WindowContainer<WindowState> { mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, mFixedRotationTransformState.mInsetsState, mFixedRotationTransformState.mBarContentFrames); - onConfigurationChanged(getParent().getConfiguration()); - notifyFixedRotationTransform(true /* enabled */); + onFixedRotationStatePrepared(); } /** @@ -553,12 +558,29 @@ class WindowToken extends WindowContainer<WindowState> { return; } if (mFixedRotationTransformState != null) { - cleanUpFixedRotationTransformState(true /* replacing */); + mFixedRotationTransformState.disassociate(this); } mFixedRotationTransformState = fixedRotationState; fixedRotationState.mAssociatedTokens.add(this); - onConfigurationChanged(getParent().getConfiguration()); + onFixedRotationStatePrepared(); + } + + /** + * Makes the rotated states take effect for this window container and its client process. + * This should only be called when {@link #mFixedRotationTransformState} is non-null. + */ + private void onFixedRotationStatePrepared() { + // Send the adjustment info first so when the client receives configuration change, it can + // get the rotated display metrics. notifyFixedRotationTransform(true /* enabled */); + // Resolve the rotated configuration. + onConfigurationChanged(getParent().getConfiguration()); + final ActivityRecord r = asActivityRecord(); + if (r != null && r.hasProcess()) { + // The application needs to be configured as in a rotated environment for compatibility. + // This registration will send the rotated configuration to its process. + r.app.registerActivityConfigurationListener(r); + } } /** @@ -609,21 +631,12 @@ class WindowToken extends WindowContainer<WindowState> { // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState( - false /* replacing */); + final WindowToken token = state.mAssociatedTokens.get(i); + token.mFixedRotationTransformState = null; + token.notifyFixedRotationTransform(false /* enabled */); } } - private void cleanUpFixedRotationTransformState(boolean replacing) { - if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) { - // The state is not only used by self. Make sure to leave the influence by others. - mFixedRotationTransformState.mAssociatedTokens.remove(this); - mFixedRotationTransformState.mRotatedContainers.remove(this); - } - mFixedRotationTransformState = null; - notifyFixedRotationTransform(false /* enabled */); - } - /** Notifies application side to enable or disable the rotation adjustment of display info. */ private void notifyFixedRotationTransform(boolean enabled) { FixedRotationAdjustments adjustments = null; @@ -687,8 +700,9 @@ class WindowToken extends WindowContainer<WindowState> { if (!isFixedRotationTransforming()) { return null; } - return new FixedRotationAdjustments(mFixedRotationTransformState.mDisplayInfo.rotation, - mFixedRotationTransformState.mDisplayInfo.displayCutout); + final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo; + return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth, + displayInfo.appHeight, displayInfo.displayCutout); } @Override diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 7e9e11d209a6..678308af34ea 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -29,12 +29,16 @@ #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <binder/IPCThreadState.h> #include <jni.h> #include <processgroup/processgroup.h> using android::base::StringPrintf; using android::base::WriteStringToFile; +#define SYNC_RECEIVED_WHILE_FROZEN (1) +#define ASYNC_RECEIVED_WHILE_FROZEN (2) + namespace android { // This performs per-process reclaim on all processes belonging to non-app UIDs. @@ -90,11 +94,45 @@ static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal( } } +static void com_android_server_am_CachedAppOptimizer_freezeBinder( + JNIEnv *env, jobject clazz, jint pid, jboolean freeze) { + + if (IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */) != 0) { + jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder"); + } +} + +static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env, + jobject clazz, jint pid) { + bool syncReceived = false, asyncReceived = false; + + int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived); + + if (error < 0) { + jniThrowException(env, "java/lang/RuntimeException", strerror(error)); + } + + jint retVal = 0; + + if(syncReceived) { + retVal |= SYNC_RECEIVED_WHILE_FROZEN;; + } + + if(asyncReceived) { + retVal |= ASYNC_RECEIVED_WHILE_FROZEN; + } + + return retVal; +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"enableFreezerInternal", "(Z)V", (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, + {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, + {"getBinderFreezeInfo", "(I)I", + (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo} }; int register_android_server_am_CachedAppOptimizer(JNIEnv* env) diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index a5f0d045948c..e355a20907d8 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -306,6 +306,7 @@ IncrementalService::~IncrementalService() { } mJobCondition.notify_all(); mJobProcessor.join(); + mLooper->wake(); mCmdLooperThread.join(); mTimedQueue->stop(); // Ensure that mounts are destroyed while the service is still valid. @@ -1378,7 +1379,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) { } void IncrementalService::runCmdLooper() { - constexpr auto kTimeoutMsecs = 1000; + constexpr auto kTimeoutMsecs = -1; while (mRunning.load(std::memory_order_relaxed)) { mLooper->pollAll(kTimeoutMsecs); } diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java index 236ac8407faa..9e6cf845d144 100644 --- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java +++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java @@ -86,8 +86,8 @@ class ShareTargetPredictor extends AppTargetPredictor { return; } List<ShareTarget> shareTargets = getDirectShareTargets(); - SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter), - System.currentTimeMillis()); + SharesheetModelScorer.computeScoreForDirectShare(shareTargets, + getShareEventType(mIntentFilter), System.currentTimeMillis()); Collections.sort(shareTargets, Comparator.comparing(ShareTarget::getScore, reverseOrder()) .thenComparing(t -> t.getAppTarget().getRank())); diff --git a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java index c77843cfb044..d4a502da56c7 100644 --- a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java +++ b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.usage.UsageEvents; +import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.Pair; import android.util.Range; @@ -27,12 +28,14 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ChooserActivity; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.server.people.data.AppUsageStatsData; import com.android.server.people.data.DataManager; import com.android.server.people.data.Event; import java.time.Duration; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -46,6 +49,7 @@ class SharesheetModelScorer { private static final String TAG = "SharesheetModelScorer"; private static final boolean DEBUG = false; private static final Integer RECENCY_SCORE_COUNT = 6; + private static final Integer NATIVE_RANK_COUNT = 2; private static final float RECENCY_INITIAL_BASE_SCORE = 0.4F; private static final float RECENCY_SCORE_INITIAL_DECAY = 0.05F; private static final float RECENCY_SCORE_SUBSEQUENT_DECAY = 0.02F; @@ -174,6 +178,77 @@ class SharesheetModelScorer { postProcess(shareTargets, targetsLimit, dataManager, callingUserId); } + /** + * Computes ranking score for direct sharing. Update + * {@link ShareTargetPredictor.ShareTargetScore}. + */ + static void computeScoreForDirectShare(List<ShareTargetPredictor.ShareTarget> shareTargets, + int shareEventType, long now) { + computeScore(shareTargets, shareEventType, now); + promoteTopNativeRankedShortcuts(shareTargets); + } + + /** + * Promotes top (NATIVE_RANK_COUNT) shortcuts for each package and class, as per shortcut native + * ranking provided by apps. + */ + private static void promoteTopNativeRankedShortcuts( + List<ShareTargetPredictor.ShareTarget> shareTargets) { + float topShortcutBonus = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER, + 0f); + float secondTopShortcutBonus = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER, + 0f); + // Populates a map which key is a packageName and className pair, value is a max heap + // containing top (NATIVE_RANK_COUNT) shortcuts as per shortcut native ranking provided + // by apps. + Map<Pair<String, String>, PriorityQueue<ShareTargetPredictor.ShareTarget>> + topNativeRankedShareTargetMap = new ArrayMap<>(); + for (ShareTargetPredictor.ShareTarget shareTarget : shareTargets) { + Pair<String, String> key = new Pair<>(shareTarget.getAppTarget().getPackageName(), + shareTarget.getAppTarget().getClassName()); + if (!topNativeRankedShareTargetMap.containsKey(key)) { + topNativeRankedShareTargetMap.put(key, + new PriorityQueue<>(NATIVE_RANK_COUNT, + Collections.reverseOrder(Comparator.comparingInt( + p -> p.getAppTarget().getRank())))); + } + PriorityQueue<ShareTargetPredictor.ShareTarget> rankMaxHeap = + topNativeRankedShareTargetMap.get(key); + if (rankMaxHeap.isEmpty() || shareTarget.getAppTarget().getRank() + < rankMaxHeap.peek().getAppTarget().getRank()) { + if (rankMaxHeap.size() == NATIVE_RANK_COUNT) { + rankMaxHeap.poll(); + } + rankMaxHeap.offer(shareTarget); + } + } + for (PriorityQueue<ShareTargetPredictor.ShareTarget> maxHeap : + topNativeRankedShareTargetMap.values()) { + while (!maxHeap.isEmpty()) { + ShareTargetPredictor.ShareTarget target = maxHeap.poll(); + float bonus = maxHeap.isEmpty() ? topShortcutBonus : secondTopShortcutBonus; + target.setScore(probOR(target.getScore(), bonus)); + + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: promote top shortcut as per native ranking," + + "packageName: %s, className: %s, shortcutId: %s, bonus:%.2f," + + "total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getAppTarget().getShortcutInfo() != null + ? target.getAppTarget().getShortcutInfo().getId() : null, + bonus, + target.getScore())); + } + } + } + } + private static void postProcess(List<ShareTargetPredictor.ShareTarget> shareTargets, int targetsLimit, @NonNull DataManager dataManager, @UserIdInt int callingUserId) { // Populates a map which key is package name and value is list of shareTargets descended diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java index 27a116c8043e..1586a33ba0e9 100644 --- a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java +++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java @@ -34,7 +34,8 @@ public final class SystemCaptionsManagerService extends context, com.android.internal.R.string.config_defaultSystemCaptionsManagerService), /*disallowProperty=*/ null, - /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER); + /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER + | PACKAGE_RESTART_POLICY_REFRESH_EAGER); } @Override diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index ca8e50aa19c9..7bd0201b997a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -1053,6 +1053,49 @@ public class AlarmManagerServiceTest { } } + @Test + public void nonWakeupAlarmsDeferred() throws Exception { + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + pis[i] = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]); + } + doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong()); + // Advance time past all expirations. + mNowElapsedTest += numAlarms + 5; + mTestTimer.expire(); + assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size()); + + // These alarms should be sent on interactive state change to true + mService.interactiveStateChangedLocked(false); + mService.interactiveStateChangedLocked(true); + + for (int i = 0; i < numAlarms; i++) { + verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(), + any(Handler.class), isNull(), any()); + } + } + + @Test + public void alarmCountOnPendingNonWakeupAlarmsRemoved() throws Exception { + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + pis[i] = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]); + } + doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong()); + // Advance time past all expirations. + mNowElapsedTest += numAlarms + 5; + mTestTimer.expire(); + assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size()); + for (int i = 0; i < numAlarms; i++) { + mService.removeLocked(pis[i], null); + assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0)); + } + } + @After public void tearDown() { if (mMockingSession != null) { diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java index 45fff48ade55..605878d85bce 100644 --- a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -28,9 +29,13 @@ import static org.mockito.Mockito.when; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetId; import android.app.usage.UsageEvents; +import android.content.Context; +import android.content.pm.ShortcutInfo; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.util.Range; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.server.people.data.AppUsageStatsData; import com.android.server.people.data.DataManager; import com.android.server.people.data.Event; @@ -121,6 +126,13 @@ public final class SharesheetModelScorerTest { private ShareTargetPredictor.ShareTarget mShareTarget5; private ShareTargetPredictor.ShareTarget mShareTarget6; + private ShareTargetPredictor.ShareTarget mShareShortcutTarget1; + private ShareTargetPredictor.ShareTarget mShareShortcutTarget2; + private ShareTargetPredictor.ShareTarget mShareShortcutTarget3; + private ShareTargetPredictor.ShareTarget mShareShortcutTarget4; + private ShareTargetPredictor.ShareTarget mShareShortcutTarget5; + private ShareTargetPredictor.ShareTarget mShareShortcutTarget6; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -154,6 +166,46 @@ public final class SharesheetModelScorerTest { new AppTargetId("cls2#pkg3"), PACKAGE_3, UserHandle.of(USER_ID)) .setClassName(CLASS_2).build(), null, null); + + mShareShortcutTarget1 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg1#1"), buildShortcutInfo(PACKAGE_1, 0, "1")) + .setClassName(CLASS_1).setRank(2).build(), + mEventHistory1, null); + mShareShortcutTarget2 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg1#2"), buildShortcutInfo(PACKAGE_1, 0, "2")) + .setClassName(CLASS_1).setRank(1).build(), + mEventHistory2, null); + mShareShortcutTarget3 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg1#3"), buildShortcutInfo(PACKAGE_1, 0, "3")) + .setClassName(CLASS_1).setRank(0).build(), + mEventHistory3, null); + mShareShortcutTarget4 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg2#1"), buildShortcutInfo(PACKAGE_2, 0, "1")) + .setClassName(CLASS_1).setRank(2).build(), + mEventHistory4, null); + mShareShortcutTarget5 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg2#2"), buildShortcutInfo(PACKAGE_2, 0, "2")) + .setClassName(CLASS_1).setRank(1).build(), + mEventHistory5, null); + mShareShortcutTarget6 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg2#3"), buildShortcutInfo(PACKAGE_2, 0, "3")) + .setClassName(CLASS_1).setRank(3).build(), + null, null); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER, + Float.toString(0f), + true /* makeDefault*/); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER, + Float.toString(0f), + true /* makeDefault*/); } @Test @@ -433,6 +485,101 @@ public final class SharesheetModelScorerTest { assertEquals(0f, mShareTarget6.getScore(), DELTA); } + @Test + public void testComputeScoreForDirectShare() { + // Frequency and recency + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + + when(mEventIndex1.getActiveTimeSlots()).thenReturn( + List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO)); + when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO)); + when(mEventIndex4.getActiveTimeSlots()).thenReturn( + List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO)); + when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of()); + + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + + // Frequency of the same mime type + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + + when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO)); + when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of()); + when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO)); + when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of()); + + SharesheetModelScorer.computeScore( + List.of(mShareShortcutTarget1, mShareShortcutTarget2, mShareShortcutTarget3, + mShareShortcutTarget4, mShareShortcutTarget5, mShareShortcutTarget6), + Event.TYPE_SHARE_TEXT, + NOW); + + // Verification + assertEquals(0.514f, mShareShortcutTarget1.getScore(), DELTA); + assertEquals(0.475125f, mShareShortcutTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareShortcutTarget3.getScore(), DELTA); + assertEquals(0.4411f, mShareShortcutTarget4.getScore(), DELTA); + assertEquals(0f, mShareShortcutTarget5.getScore(), DELTA); + assertEquals(0f, mShareShortcutTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForDirectShare_promoteTopNativeRankedShortcuts() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER, + Float.toString(0.4f), + true /* makeDefault*/); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER, + Float.toString(0.3f), + true /* makeDefault*/); + + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + + SharesheetModelScorer.computeScoreForDirectShare( + List.of(mShareShortcutTarget1, mShareShortcutTarget2, mShareShortcutTarget3, + mShareShortcutTarget4, mShareShortcutTarget5, mShareShortcutTarget6), + Event.TYPE_SHARE_TEXT, 20); + + assertEquals(0f, mShareShortcutTarget1.getScore(), DELTA); + assertEquals(0.3f, mShareShortcutTarget2.getScore(), DELTA); + assertEquals(0.4f, mShareShortcutTarget3.getScore(), DELTA); + assertEquals(0.3f, mShareShortcutTarget4.getScore(), DELTA); + assertEquals(0.4f, mShareShortcutTarget5.getScore(), DELTA); + assertEquals(0f, mShareShortcutTarget6.getScore(), DELTA); + } + + private static ShortcutInfo buildShortcutInfo(String packageName, int userId, String id) { + Context mockContext = mock(Context.class); + when(mockContext.getPackageName()).thenReturn(packageName); + when(mockContext.getUserId()).thenReturn(userId); + when(mockContext.getUser()).thenReturn(UserHandle.of(userId)); + ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id).setShortLabel(id); + return builder.build(); + } + private static UsageEvents.Event createUsageEvent(String packageName) { UsageEvents.Event e = new UsageEvents.Event(); e.mPackage = packageName; diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 480a02de064a..af5a539450ac 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -382,7 +382,7 @@ public class AppsFilterTest { } @Test - public void testForceQueryable_DoesntFilter() throws Exception { + public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); @@ -390,7 +390,8 @@ public class AppsFilterTest { appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID, + setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID); @@ -398,6 +399,24 @@ public class AppsFilterTest { SYSTEM_USER)); } + + @Test + public void testForceQueryable_NonSystemFilters() throws Exception { + final AppsFilter appsFilter = + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package"), DUMMY_CALLING_APPID); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + @Test public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { final AppsFilter appsFilter = diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 66ca839081bc..0ccc02663dc5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -105,7 +105,7 @@ public class UserManagerServiceUserInfoTest { UserData read = mUserManagerService.readUserLP( data.info.id, new ByteArrayInputStream(bytes)); - assertUserInfoEquals(data.info, read.info); + assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false); } @Test @@ -123,7 +123,16 @@ public class UserManagerServiceUserInfoTest { UserInfo read = UserInfo.CREATOR.createFromParcel(in); in.recycle(); - assertUserInfoEquals(info, read); + assertUserInfoEquals(info, read, /* parcelCopy= */ true); + } + + @Test + public void testCopyConstructor() throws Exception { + UserInfo info = createUser(); + + UserInfo copy = new UserInfo(info); + + assertUserInfoEquals(info, copy, /* parcelCopy= */ false); } @Test @@ -227,10 +236,11 @@ public class UserManagerServiceUserInfoTest { user.partial = true; user.guestToRemove = true; user.preCreated = true; + user.convertedFromPreCreated = true; return user; } - private void assertUserInfoEquals(UserInfo one, UserInfo two) { + private void assertUserInfoEquals(UserInfo one, UserInfo two, boolean parcelCopy) { assertEquals("Id not preserved", one.id, two.id); assertEquals("Name not preserved", one.name, two.name); assertEquals("Icon path not preserved", one.iconPath, two.iconPath); @@ -238,11 +248,17 @@ public class UserManagerServiceUserInfoTest { assertEquals("UserType not preserved", one.userType, two.userType); assertEquals("profile group not preserved", one.profileGroupId, two.profileGroupId); - assertEquals("restricted profile parent not preseved", one.restrictedProfileParentId, + assertEquals("restricted profile parent not preserved", one.restrictedProfileParentId, two.restrictedProfileParentId); - assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge); - assertEquals("partial not preseved", one.partial, two.partial); - assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove); - assertEquals("preCreated not preseved", one.preCreated, two.preCreated); + assertEquals("profile badge not preserved", one.profileBadge, two.profileBadge); + assertEquals("partial not preserved", one.partial, two.partial); + assertEquals("guestToRemove not preserved", one.guestToRemove, two.guestToRemove); + assertEquals("preCreated not preserved", one.preCreated, two.preCreated); + if (parcelCopy) { + assertFalse("convertedFromPreCreated should not be set", two.convertedFromPreCreated); + } else { + assertEquals("convertedFromPreCreated not preserved", one.convertedFromPreCreated, + two.convertedFromPreCreated); + } } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 4dec7a1a0ab9..a07e60ce838e 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -112,6 +112,8 @@ public class AppStandbyControllerTests { private static final int UID_SYSTEM_HEADFULL = 10002; private static final String PACKAGE_SYSTEM_HEADLESS = "com.example.system.headless"; private static final int UID_SYSTEM_HEADLESS = 10003; + private static final String PACKAGE_WELLBEING = "com.example.wellbeing"; + private static final int UID_WELLBEING = 10004; private static final int USER_ID = 0; private static final int USER_ID2 = 10; private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2); @@ -218,6 +220,11 @@ public class AppStandbyControllerTests { } @Override + boolean isWellbeingPackage(String packageName) { + return PACKAGE_WELLBEING.equals(packageName); + } + + @Override void updatePowerWhitelistCache() { } @@ -329,6 +336,12 @@ public class AppStandbyControllerTests { pish.packageName = PACKAGE_SYSTEM_HEADLESS; packages.add(pish); + PackageInfo piw = new PackageInfo(); + piw.applicationInfo = new ApplicationInfo(); + piw.applicationInfo.uid = UID_WELLBEING; + piw.packageName = PACKAGE_WELLBEING; + packages.add(piw); + doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt()); try { for (int i = 0; i < packages.size(); ++i) { @@ -1516,6 +1529,25 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1); } + @Test + public void testWellbeingAppElevated() { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING); + assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING); + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1); + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; + + // Make sure the default wellbeing app does not get lowered below WORKING_SET. + mController.setAppStandbyBucket(PACKAGE_WELLBEING, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_WELLBEING); + + // A non default wellbeing app should be able to fall lower than WORKING_SET. + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); } diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index b100c8482bf8..453830004ac3 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server; +import android.Manifest; import android.app.AlarmManager; import android.app.IUiModeManager; import android.content.BroadcastReceiver; @@ -24,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Handler; @@ -66,6 +68,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -227,6 +230,15 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test + public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException { + mUiManagerService.onSwitchUser(9); + when(mContext.checkCallingOrSelfPermission( + eq(Manifest.permission.INTERACT_ACROSS_USERS))) + .thenReturn(PackageManager.PERMISSION_DENIED); + assertFalse(mService.setNightModeActivated(true)); + } + + @Test public void autoNightModeSwitch_batterySaverOn() throws RemoteException { mService.setNightMode(MODE_NIGHT_NO); when(mTwilightState.isNight()).thenReturn(false); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 1ef0acd20835..a37f4be506e5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -22,7 +22,7 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; @@ -548,7 +548,7 @@ public class ActivityRecordTests extends ActivityTestsBase { final ActivityStack stack = new StackBuilder(mRootWindowContainer).build(); try { doReturn(false).when(stack).isTranslucent(any()); - assertFalse(mStack.shouldBeVisible(null /* starting */)); + assertTrue(mStack.shouldBeVisible(null /* starting */)); mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), mActivity.getConfiguration())); @@ -1694,7 +1694,7 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testGetLockTaskLaunchMode() { final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true); mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; - assertEquals(LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED, + assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED, ActivityRecord.getLockTaskLaunchMode(mActivity.info, options)); mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 1b42a0466cf7..e898c2573315 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1206,19 +1206,22 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testShouldSleepActivities() { // When focused activity and keyguard is going away, we should not sleep regardless - // of the display state + // of the display state, but keyguard-going-away should only take effects on default + // display since there is no keyguard on secondary displays (yet). verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, false /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); + verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, + true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */); // When not the focused stack, defer to display sleeping state. verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); // If keyguard is going away, defer to the display sleeping state. verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - false /* displaySleeping */, false /* expected*/); + false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); } @Test @@ -1428,9 +1431,11 @@ public class ActivityStackTests extends ActivityTestsBase { } private void verifyShouldSleepActivities(boolean focusedStack, - boolean keyguardGoingAway, boolean displaySleeping, boolean expected) { + boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, + boolean expected) { final DisplayContent display = mock(DisplayContent.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); + display.isDefaultDisplay = isDefaultDisplay; doReturn(display).when(mStack).getDisplay(); doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index dc838f1bb288..c7a8bd857674 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -28,6 +28,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.Q; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.FLAG_PRIVATE; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; @@ -95,6 +96,7 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; +import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; @@ -1147,8 +1149,10 @@ public class DisplayContentTests extends WindowTestsBase { verify(t, never()).setPosition(any(), eq(0), eq(0)); // Launch another activity before the transition is finished. - final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot) - .setDisplay(mDisplayContent).build().getTopMostActivity(); + final ActivityStack stack2 = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build(); + final ActivityRecord app2 = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setStack(stack2).setUseProcess(app.app).build(); app2.setVisible(false); mDisplayContent.mOpeningApps.add(app2); app2.setRequestedOrientation(newOrientation); @@ -1158,6 +1162,12 @@ public class DisplayContentTests extends WindowTestsBase { assertTrue(app.hasFixedRotationTransform(app2)); assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2)); + final Configuration expectedProcConfig = new Configuration(app2.app.getConfiguration()); + expectedProcConfig.windowConfiguration.setActivityType( + WindowConfiguration.ACTIVITY_TYPE_UNDEFINED); + assertEquals("The process should receive rotated configuration for compatibility", + expectedProcConfig, app2.app.getConfiguration()); + // The fixed rotation transform can only be finished when all animation finished. doReturn(false).when(app2).isAnimating(anyInt(), anyInt()); mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token); @@ -1439,6 +1449,29 @@ public class DisplayContentTests extends WindowTestsBase { mDisplayContent.ensureActivitiesVisible(null, 0, false, false); } + @Test + public void testForceDesktopMode() { + mWm.mForceDesktopModeOnExternalDisplays = true; + // Not applicable for default display + final DisplayContent defaultDisplay = mWm.mRoot.getDefaultDisplay(); + assertFalse(defaultDisplay.forceDesktopMode()); + + // Not applicable for private secondary display. + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.flags = FLAG_PRIVATE; + final DisplayContent privateDc = createNewDisplay(displayInfo); + assertFalse(privateDc.forceDesktopMode()); + + // Applicable for public secondary display. + final DisplayContent publicDc = createNewDisplay(); + assertTrue(publicDc.forceDesktopMode()); + + // Make sure forceDesktopMode() is false when the force config is disabled. + mWm.mForceDesktopModeOnExternalDisplays = false; + assertFalse(publicDc.forceDesktopMode()); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 2f3afbcb6d72..cb9b49abfe2b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -20,6 +20,8 @@ import static android.view.Gravity.BOTTOM; import static android.view.Gravity.LEFT; import static android.view.Gravity.RIGHT; import static android.view.Gravity.TOP; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_TOP_GESTURES; @@ -36,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -197,10 +200,15 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void addingWindow_throwsException_WithMultipleInsetTypes() { - WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); - win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}; + WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); + win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}; + + expectThrows(IllegalArgumentException.class, () -> addWindow(win1)); - expectThrows(IllegalArgumentException.class, () -> addWindow(win)); + WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); + win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; + + expectThrows(IllegalArgumentException.class, () -> addWindow(win2)); } @Test @@ -349,6 +357,24 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_insetParentFrameByIme() { + final InsetsState state = + mDisplayContent.getInsetsStateController().getRawInsetsState(); + state.getSource(InsetsState.ITYPE_IME).setVisible(true); + state.getSource(InsetsState.ITYPE_IME).setFrame( + 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); + mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + mWindow.mBehindIme = true; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT); + } + + @Test public void layoutWindowLw_fitDisplayCutout() { addDisplayCutout(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 7ba3fd815b2d..c87014a8a27f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -62,7 +62,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; - static final int INPUT_METHOD_WINDOW_TOP = 585; + static final int IME_HEIGHT = 415; DisplayPolicy mDisplayPolicy; diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java index ca739c0dd389..91cfd4e6a89d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java @@ -56,4 +56,12 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mImeProvider.scheduleShowImePostLayout(appWin); assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame()); } + + @Test + public void testInputMethodInputTargetCanShowIme() { + WindowState target = createWindow(null, TYPE_APPLICATION, "app"); + mDisplayContent.mInputMethodTarget = target; + mImeProvider.scheduleShowImePostLayout(target); + assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 0a27e1a1da68..73ac408f7980 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -292,12 +294,17 @@ public class InsetsStateControllerTest extends WindowTestsBase { public void testBarControllingWinChanged() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar"); + final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar"); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindow(climateBar, null, null); + getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindow(extraNavBar, null, + null); getController().onBarControlTargetChanged(app, null, app, null); InsetsSourceControl[] controls = getController().getControlsForDispatch(app); - assertEquals(2, controls.length); + assertEquals(4, controls.length); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index a137cde2d351..e345becf0499 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -168,8 +168,8 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_once() throws Exception { - // GIVEN a task record with whitelisted auth - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN a task record with allowlisted auth + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode without resuming mLockTaskController.startLockTaskMode(tr, false, TEST_UID); @@ -185,9 +185,9 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_twice() throws Exception { - // GIVEN two task records with whitelisted auth - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode on both tasks mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -205,7 +205,7 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_pinningRequest() { - // GIVEN a task record that is not whitelisted, i.e. with pinned auth + // GIVEN a task record that is not allowlisted, i.e. with pinned auth Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE); // WHEN calling startLockTaskMode @@ -236,23 +236,23 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN it's not a lock task violation to try and launch this task without clearing assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false)); - // THEN it's a lock task violation to launch another task that is not whitelisted + // THEN it's a lock task violation to launch another task that is not allowlisted assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_PINNABLE))); // THEN it's a lock task violation to launch another task that is disallowed from lock task assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_DONT_LOCK))); - // THEN it's no a lock task violation to launch another task that is whitelisted + // THEN it's no a lock task violation to launch another task that is allowlisted assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( - Task.LOCK_TASK_AUTH_WHITELISTED))); + Task.LOCK_TASK_AUTH_ALLOWLISTED))); assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_LAUNCHABLE))); // THEN it's not a lock task violation to launch another task that is priv launchable @@ -262,8 +262,8 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation_emergencyCall() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // GIVEN tasks necessary for emergency calling @@ -294,8 +294,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode() throws Exception { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN the same caller calls stopLockTaskMode @@ -311,8 +311,8 @@ public class LockTaskControllerTest { @Test(expected = SecurityException.class) public void testStopLockTaskMode_differentCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN a different caller calls stopLockTaskMode @@ -323,8 +323,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_systemCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN system calls stopLockTaskMode @@ -336,9 +336,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_twoTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -357,9 +357,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_rootTask() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -405,9 +405,9 @@ public class LockTaskControllerTest { @Test public void testClearLockedTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -434,7 +434,7 @@ public class LockTaskControllerTest { .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -454,7 +454,7 @@ public class LockTaskControllerTest { .thenReturn(true); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -471,7 +471,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -488,7 +488,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -500,45 +500,45 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskPackages() { - String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - String[] whitelist2 = {TEST_PACKAGE_NAME}; - - // No package is whitelisted initially - for (String pkg : whitelist1) { - assertFalse("Package shouldn't be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + String[] allowlist2 = {TEST_PACKAGE_NAME}; + + // No package is allowlisted initially + for (String pkg : allowlist1) { + assertFalse("Package shouldn't be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Apply whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1); + // Apply allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1); - // Assert the whitelist is applied to the correct user - for (String pkg : whitelist1) { - assertTrue("Package should be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + // Assert the allowlist is applied to the correct user + for (String pkg : allowlist1) { + assertTrue("Package should be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Update whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2); + // Update allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2); - // Assert the new whitelist is applied - assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME)); - assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); + // Assert the new allowlist is applied + assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME)); + assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); } @Test public void testUpdateLockTaskPackages_taskRemoved() throws Exception { - // GIVEN two tasks which are whitelisted initially + // GIVEN two tasks which are allowlisted initially Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true); Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false); - String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // GIVEN the tasks are launched into LockTask mode mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -548,9 +548,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr2)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing one package from whitelist - whitelist = new String[] {TEST_PACKAGE_NAME}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing one package from allowlist + allowlist = new String[] {TEST_PACKAGE_NAME}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the task running that package should be stopped verify(tr2).performClearTaskLocked(); @@ -560,9 +560,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr1)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing the last package from whitelist - whitelist = new String[] {}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing the last package from allowlist + allowlist = new String[] {}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the last task should be cleared, and the system should quit LockTask mode verify(tr1).performClearTaskLocked(); @@ -574,7 +574,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -616,7 +616,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_differentUser() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -638,7 +638,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_keyguard() { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN keyguard should be disabled @@ -704,7 +704,7 @@ public class LockTaskControllerTest { TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); // Start lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled @@ -719,15 +719,15 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS)); - // unwhitelisted package should not be allowed + // unallowlisted package should not be allowed assertFalse(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); - // update the whitelist - String[] whitelist = new String[] { TEST_PACKAGE_NAME }; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // update the allowlist + String[] allowlist = new String[] { TEST_PACKAGE_NAME }; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); - // whitelisted package should be allowed + // allowlisted package should be allowed assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); @@ -755,17 +755,17 @@ public class LockTaskControllerTest { } /** - * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest + * @param isAppAware {@code true} if the app has marked if_allowlisted in its manifest */ private Task getTaskForUpdate(String pkg, boolean isAppAware) { - final int authIfWhitelisted = isAppAware + final int authIfAllowlisted = isAppAware ? Task.LOCK_TASK_AUTH_LAUNCHABLE - : Task.LOCK_TASK_AUTH_WHITELISTED; - Task tr = getTask(pkg, authIfWhitelisted); + : Task.LOCK_TASK_AUTH_ALLOWLISTED; + Task tr = getTask(pkg, authIfAllowlisted); doAnswer((invocation) -> { - boolean isWhitelisted = - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg); - tr.mLockTaskAuth = isWhitelisted ? authIfWhitelisted : Task.LOCK_TASK_AUTH_PINNABLE; + boolean isAllowlisted = + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg); + tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE; return null; }).when(tr).setLockTaskAuth(); return tr; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index c848736a64c2..51db099676b0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -896,24 +896,6 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); } - @Test - public void testResumeFocusedStackOnSleepingDisplay() { - // Create an activity on secondary display. - final TestDisplayContent secondDisplay = addNewDisplayContentAt( - DisplayContent.POSITION_TOP); - final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build(); - spyOn(activity); - spyOn(stack); - - // Cannot resumed activities on secondary display if the display should sleep. - doReturn(true).when(secondDisplay).shouldSleep(); - mRootWindowContainer.resumeFocusedStacksTopActivities(); - verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any()); - verify(activity, never()).makeActiveIfNeeded(any()); - } - /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases. diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java index ef74861e9422..25ba6db38e05 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java @@ -68,6 +68,7 @@ import com.android.compatibility.common.util.SystemUtil; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -142,6 +143,9 @@ public class ScreenDecorWindowTests { assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness); } + // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported. + // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon. + @Ignore @Test public void testMultipleDecors() { // Test 2 decor windows on-top. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 24950ce6a882..a46e6d35ee97 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -266,6 +266,15 @@ public class WindowProcessControllerTests extends ActivityTestsBase { mWpc.onMergedOverrideConfigurationChanged(config); assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType()); assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType()); + + final int globalSeq = 100; + mRootWindowContainer.getConfiguration().seq = globalSeq; + invertOrientation(mWpc.getConfiguration()); + new ActivityBuilder(mService).setCreateTask(true).setUseProcess(mWpc).build(); + + assertTrue(mWpc.registeredForActivityConfigChanges()); + assertEquals("Config seq of process should not be affected by activity", + mWpc.getConfiguration().seq, globalSeq); } private TestDisplayContent createTestDisplayContentInContainer() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 0896db4b5532..0ab99122e21f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -78,6 +78,10 @@ public class WindowTokenTests extends WindowTestsBase { assertFalse(token.hasWindow(window12)); assertTrue(token.hasWindow(window2)); assertTrue(token.hasWindow(window3)); + + // The child windows should have the same window token as their parents. + assertEquals(window1.mToken, window11.mToken); + assertEquals(window1.mToken, window12.mToken); } @Test diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 321657d5d626..a5b676012ea9 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -183,6 +183,7 @@ public class UsageStatsService extends SystemService implements private static class ActivityData { private final String mTaskRootPackage; private final String mTaskRootClass; + public int lastEvent = Event.NONE; private ActivityData(String taskRootPackage, String taskRootClass) { mTaskRootPackage = taskRootPackage; mTaskRootClass = taskRootClass; @@ -787,6 +788,7 @@ public class UsageStatsService extends SystemService implements switch (event.mEventType) { case Event.ACTIVITY_RESUMED: case Event.ACTIVITY_PAUSED: + case Event.ACTIVITY_STOPPED: uid = mPackageManagerInternal.getPackageUid(event.mPackage, 0, userId); break; default: @@ -819,8 +821,10 @@ public class UsageStatsService extends SystemService implements .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND); // check if this activity has already been resumed if (mVisibleActivities.get(event.mInstanceId) != null) break; - mVisibleActivities.put(event.mInstanceId, - new ActivityData(event.mTaskRootPackage, event.mTaskRootClass)); + final ActivityData resumedData = new ActivityData(event.mTaskRootPackage, + event.mTaskRootClass); + resumedData.lastEvent = Event.ACTIVITY_RESUMED; + mVisibleActivities.put(event.mInstanceId, resumedData); try { switch(mUsageSource) { case USAGE_SOURCE_CURRENT_ACTIVITY: @@ -836,16 +840,17 @@ public class UsageStatsService extends SystemService implements } break; case Event.ACTIVITY_PAUSED: - if (event.mTaskRootPackage == null) { - // Task Root info is missing. Repair the event based on previous data - final ActivityData prevData = mVisibleActivities.get(event.mInstanceId); - if (prevData == null) { - Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage - + "/" + event.mClass + " event : " + event.mEventType - + " instanceId : " + event.mInstanceId + ")"); - } else { - event.mTaskRootPackage = prevData.mTaskRootPackage; - event.mTaskRootClass = prevData.mTaskRootClass; + final ActivityData pausedData = mVisibleActivities.get(event.mInstanceId); + if (pausedData == null) { + Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage + + "/" + event.mClass + " event : " + event.mEventType + + " instanceId : " + event.mInstanceId + ")"); + } else { + pausedData.lastEvent = Event.ACTIVITY_PAUSED; + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + event.mTaskRootPackage = pausedData.mTaskRootPackage; + event.mTaskRootClass = pausedData.mTaskRootClass; } } FrameworkStatsLog.write( @@ -868,6 +873,16 @@ public class UsageStatsService extends SystemService implements return; } + if (prevData.lastEvent != Event.ACTIVITY_PAUSED) { + FrameworkStatsLog.write( + FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED, + uid, + event.mPackage, + event.mClass, + FrameworkStatsLog + .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND); + } + ArraySet<String> tokens; synchronized (mUsageReporters) { tokens = mUsageReporters.removeReturnOld(event.mInstanceId); diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 6c13cd799bc2..fe0f7b80997e 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -697,7 +697,7 @@ public class SoundTriggerService extends SystemService { synchronized (mLock) { ModuleProperties properties = mSoundTriggerHelper.getModuleProperties(); sEventLogger.log(new SoundTriggerLogger.StringEvent( - "getModuleProperties(): " + properties.toString())); + "getModuleProperties(): " + properties)); return properties; } } @@ -1284,32 +1284,25 @@ public class SoundTriggerService extends SystemService { * @return The initialized AudioRecord */ private @NonNull AudioRecord createAudioRecordForEvent( - @NonNull SoundTrigger.GenericRecognitionEvent event) { + @NonNull SoundTrigger.GenericRecognitionEvent event) + throws IllegalArgumentException, UnsupportedOperationException { AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder(); attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD); AudioAttributes attributes = attributesBuilder.build(); - // Use same AudioFormat processing as in RecognitionEvent.fromParcel AudioFormat originalFormat = event.getCaptureFormat(); - AudioFormat captureFormat = (new AudioFormat.Builder()) - .setChannelMask(originalFormat.getChannelMask()) - .setEncoding(originalFormat.getEncoding()) - .setSampleRate(originalFormat.getSampleRate()) - .build(); - - int bufferSize = AudioRecord.getMinBufferSize( - captureFormat.getSampleRate() == AudioFormat.SAMPLE_RATE_UNSPECIFIED - ? AudioFormat.SAMPLE_RATE_HZ_MAX - : captureFormat.getSampleRate(), - captureFormat.getChannelCount() == 2 - ? AudioFormat.CHANNEL_IN_STEREO - : AudioFormat.CHANNEL_IN_MONO, - captureFormat.getEncoding()); sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent")); - return new AudioRecord(attributes, captureFormat, bufferSize, - event.getCaptureSession()); + return (new AudioRecord.Builder()) + .setAudioAttributes(attributes) + .setAudioFormat((new AudioFormat.Builder()) + .setChannelMask(originalFormat.getChannelMask()) + .setEncoding(originalFormat.getEncoding()) + .setSampleRate(originalFormat.getSampleRate()) + .build()) + .setSessionId(event.getCaptureSession()) + .build(); } @Override @@ -1335,12 +1328,16 @@ public class SoundTriggerService extends SystemService { // execute if throttled: () -> { if (event.isCaptureAvailable()) { - AudioRecord capturedData = createAudioRecordForEvent(event); - - // Currently we need to start and release the audio record to reset - // the DSP even if we don't want to process the event - capturedData.startRecording(); - capturedData.release(); + try { + // Currently we need to start and release the audio record to reset + // the DSP even if we don't want to process the eve + AudioRecord capturedData = createAudioRecordForEvent(event); + capturedData.startRecording(); + capturedData.release(); + } catch (IllegalArgumentException | UnsupportedOperationException e) { + Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event + + "), failed to create AudioRecord"); + } } })); } diff --git a/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl b/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl new file mode 100644 index 000000000000..50bbf4c41284 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telecom; + +/* + * Adapter interface for using DeviceIdleController, since the PowerWhitelistManager is not + * directly accessible in the SYSTEM process. + */ +interface IDeviceIdleControllerAdapter { + void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle, + String reason); +}
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl b/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl new file mode 100644 index 000000000000..b56010696361 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telecom; + +import com.android.internal.telecom.IDeviceIdleControllerAdapter; + +/* + * Interface used to retrieve services that are only accessible via LocalService in the SYSTEM + * process. + */ +interface IInternalServiceRetriever { + IDeviceIdleControllerAdapter getDeviceIdleController(); +} diff --git a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl new file mode 100644 index 000000000000..eda0f5b24958 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telecom; + +import com.android.internal.telecom.ITelecomService; +import com.android.internal.telecom.IInternalServiceRetriever; + +/* + * Internal interface for getting an instance of the ITelecomService for external publication. + * Allows the TelecomLoaderService to pass additional dependencies required for creation. + */ +interface ITelecomLoader { + ITelecomService createTelecomService(IInternalServiceRetriever retriever); +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5d79775f686e..74d7f5205209 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -32,6 +32,7 @@ import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsSsData; import com.android.internal.telephony.ICarrierConfigLoader; import com.android.telephony.Rlog; @@ -66,6 +67,20 @@ public class CarrierConfigManager { public static final String EXTRA_SUBSCRIPTION_INDEX = SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX; + /** + * Service class flag if not specify a service class. + * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK + * @hide + */ + public static final int SERVICE_CLASS_NONE = ImsSsData.SERVICE_CLASS_NONE; + + /** + * Service class flag for voice telephony. + * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK + * @hide + */ + public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE; + private final Context mContext; /** @@ -212,6 +227,19 @@ public class CarrierConfigManager { "call_barring_supports_deactivate_all_bool"; /** + * Specifies the service class for call barring service. Default value is + * {@link #SERVICE_CLASS_VOICE}. + * The value set as below: + * <ul> + * <li>0: {@link #SERVICE_CLASS_NONE}</li> + * <li>1: {@link #SERVICE_CLASS_VOICE}</li> + * </ul> + * @hide + */ + public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = + "call_barring_default_service_class_int"; + + /** * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED * events from the Sim. * If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and @@ -3849,10 +3877,33 @@ public class CarrierConfigManager { * Indicating whether DUN APN should be disabled when the device is roaming. In that case, * the default APN (i.e. internet) will be used for tethering. * + * This config is only available when using Preset APN(not user edited) as Preferred APN. + * + * @hide + */ + public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = + "disable_dun_apn_while_roaming_with_preset_apn_bool"; + + /** + * Where there is no preferred APN, specifies the carrier's default preferred APN. + * Specifies the {@link android.provider.Telephony.Carriers.APN} of the default preferred apn. + * + * This config is only available with Preset APN(not user edited). + * + * @hide + */ + public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING = + "default_preferred_apn_name_string"; + + /** + * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values + * for IPv4 and IPv6 if both are sent. + * TODO: remove in later release + * * @hide */ - public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING = - "disable_dun_apn_while_roaming"; + public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED = + "use_lower_mtu_value_if_both_received"; /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3926,6 +3977,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); + sDefaults.putInt(KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT, SERVICE_CLASS_VOICE); sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL, true); @@ -4388,7 +4440,9 @@ public class CarrierConfigManager { "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3" }); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); - sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING, false); + sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); + sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); + sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); } /** diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index a8206e69efd2..45a67b39ea55 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -138,7 +138,11 @@ public final class CellIdentityNr extends CellIdentity { @NonNull @Override public CellLocation asCellLocation() { - return new GsmCellLocation(); + GsmCellLocation cl = new GsmCellLocation(); + int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1; + cl.setLacAndCid(tac, -1); + cl.setPsc(0); + return cl; } @Override diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 8ae1ee99b060..3d51d7cf7af7 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1888,8 +1888,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -1941,8 +1941,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -2010,8 +2010,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -2088,8 +2088,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -2126,8 +2126,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -2210,8 +2210,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -2247,8 +2247,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -3853,8 +3853,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -3891,8 +3891,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -4145,8 +4145,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. @@ -4184,8 +4184,8 @@ public class TelephonyManager { * * <p>Starting with API level 29, persistent device identifiers are guarded behind additional * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of - * the following requirements is met: + * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This + * method can be invoked if one of the following requirements is met: * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java index 30cea0e6dd59..bc8ee1dd9359 100644 --- a/telephony/java/android/telephony/gsm/GsmCellLocation.java +++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java @@ -55,7 +55,7 @@ public class GsmCellLocation extends CellLocation { } /** - * @return gsm cell id, -1 if unknown, 0xffff max legal value + * @return gsm cell id, -1 if unknown or invalid, 0xffff max legal value */ public int getCid() { return mCid; diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 2be4ae6bb214..3ccbad84f2e0 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -48,6 +48,9 @@ java_test_host { name: "MultiUserRollbackTest", srcs: ["MultiUserRollbackTest/src/**/*.java"], libs: ["tradefed"], + static_libs: [ + "frameworks-base-hostutils", + ], test_suites: ["general-tests"], test_config: "MultiUserRollbackTest.xml", } diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java index 42b886f0774f..f16084744853 100644 --- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java +++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java @@ -24,6 +24,7 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +41,9 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { private static final long SWITCH_USER_COMPLETED_NUMBER_OF_POLLS = 60; private static final long SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS = 1000; + @Rule + public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); + @After public void tearDown() throws Exception { removeSecondaryUserIfNecessary(); @@ -59,6 +63,30 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { runPhaseForUsers("testBasic", mSecondaryUserId); } + /** + * Tests staged install/rollback works correctly on the 2nd user. + */ + @Test + public void testStagedRollback() throws Exception { + runPhaseForUsers("testStagedRollback_Phase1", mSecondaryUserId); + getDevice().reboot(); + + // Need to unlock the user for device tests to run successfully + getDevice().startUser(mSecondaryUserId); + awaitUserUnlocked(mSecondaryUserId); + runPhaseForUsers("testStagedRollback_Phase2", mSecondaryUserId); + getDevice().reboot(); + + getDevice().startUser(mSecondaryUserId); + awaitUserUnlocked(mSecondaryUserId); + runPhaseForUsers("testStagedRollback_Phase3", mSecondaryUserId); + getDevice().reboot(); + + getDevice().startUser(mSecondaryUserId); + awaitUserUnlocked(mSecondaryUserId); + runPhaseForUsers("testStagedRollback_Phase4", mSecondaryUserId); + } + @Test public void testMultipleUsers() throws Exception { runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId); diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java index 8641f4d4013a..5d133a4de13d 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java @@ -115,4 +115,32 @@ public class MultiUserRollbackTest { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); InstallUtils.processUserData(TestApp.A); } + + @Test + public void testStagedRollback_Phase1() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + Install.single(TestApp.A1).setStaged().commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + } + + @Test + public void testStagedRollback_Phase2() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + } + + @Test + public void testStagedRollback_Phase3() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + RollbackInfo rollback = RollbackUtils.waitForAvailableRollback(TestApp.A); + assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1)); + RollbackUtils.rollback(rollback.getRollbackId()); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + } + + @Test + public void testStagedRollback_Phase4() { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + } } diff --git a/tests/net/Android.bp b/tests/net/Android.bp index 124b6609f687..0fe84abcbc7b 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -63,6 +63,7 @@ android_test { "services.net", ], libs: [ + "android.net.ipsec.ike.stubs.module_lib", "android.test.runner", "android.test.base", "android.test.mock", diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index de1c5759ee87..91ffa8e9d0d8 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -20,6 +20,7 @@ import static android.content.pm.UserInfo.FLAG_ADMIN; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_RESTRICTED; +import static android.net.ConnectivityManager.NetworkCallback; import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; @@ -45,7 +46,9 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -66,6 +69,7 @@ import android.net.Ikev2VpnProfile; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; +import android.net.IpSecTunnelInterfaceResponse; import android.net.LinkProperties; import android.net.LocalSocket; import android.net.Network; @@ -75,6 +79,8 @@ import android.net.RouteInfo; import android.net.UidRange; import android.net.VpnManager; import android.net.VpnService; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.ConditionVariable; @@ -101,6 +107,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -150,6 +157,11 @@ public class VpnTest { private static final String TEST_VPN_IDENTITY = "identity"; private static final byte[] TEST_VPN_PSK = "psk".getBytes(); + private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE); + private static final String TEST_IFACE_NAME = "TEST_IFACE"; + private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345; + private static final long TEST_TIMEOUT_MS = 500L; + /** * Names and UIDs for some fake packages. Important points: * - UID is ordered increasing. @@ -227,6 +239,13 @@ public class VpnTest { // Deny all appops by default. when(mAppOps.noteOpNoThrow(anyInt(), anyInt(), anyString())) .thenReturn(AppOpsManager.MODE_IGNORED); + + // Setup IpSecService + final IpSecTunnelInterfaceResponse tunnelResp = + new IpSecTunnelInterfaceResponse( + IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME); + when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any())) + .thenReturn(tunnelResp); } @Test @@ -988,6 +1007,52 @@ public class VpnTest { eq(AppOpsManager.MODE_IGNORED)); } + private NetworkCallback triggerOnAvailableAndGetCallback() { + final ArgumentCaptor<NetworkCallback> networkCallbackCaptor = + ArgumentCaptor.forClass(NetworkCallback.class); + verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)) + .requestNetwork(any(), networkCallbackCaptor.capture()); + + final NetworkCallback cb = networkCallbackCaptor.getValue(); + cb.onAvailable(TEST_NETWORK); + return cb; + } + + @Test + public void testStartPlatformVpnAuthenticationFailed() throws Exception { + final ArgumentCaptor<IkeSessionCallback> captor = + ArgumentCaptor.forClass(IkeSessionCallback.class); + final IkeProtocolException exception = mock(IkeProtocolException.class); + when(exception.getErrorType()) + .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED); + + final Vpn vpn = startLegacyVpn(mVpnProfile); + final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent + // state + verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS)) + .createIkeSession(any(), any(), any(), any(), captor.capture(), any()); + final IkeSessionCallback ikeCb = captor.getValue(); + ikeCb.onClosedExceptionally(exception); + + verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); + assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + } + + @Test + public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { + when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) + .thenThrow(new IllegalArgumentException()); + final Vpn vpn = startLegacyVpn(mVpnProfile); + final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent + // state + verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); + assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + } + private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) { assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore)); diff --git a/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java new file mode 100644 index 000000000000..5bfc75203ae3 --- /dev/null +++ b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.rollback.host; + +import com.android.ddmlib.Log; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.rules.ExternalResource; + +public final class AbandonSessionsRule extends ExternalResource { + private static final String TAG = "AbandonSessionsRule"; + private final BaseHostJUnit4Test mHost; + + public AbandonSessionsRule(BaseHostJUnit4Test host) { + mHost = host; + } + + @Override + protected void before() throws Throwable { + abandonSessions(mHost.getDevice()); + } + + @Override + protected void after() { + try { + abandonSessions(mHost.getDevice()); + } catch (Exception e) { + mHost.getDevice().logOnDevice(TAG, Log.LogLevel.ERROR, + "%s", "Failed to abandon sessions"); + } + } + + /** + * Abandons all sessions to prevent interference in our tests. + */ + private static void abandonSessions(ITestDevice device) throws Exception { + // No point in abandoning applied or failed sessions. We care about ready sessions only. + String cmdListReadySessions = + "pm list staged-sessions --only-sessionid --only-parent --only-ready"; + String output = device.executeShellCommand(cmdListReadySessions); + if (output.trim().isEmpty()) { + // No sessions to abandon + return; + } + // Ensure we have sufficient privilege to abandon sessions from other apps + device.enableAdbRoot(); + device.executeShellCommand("for i in $(" + cmdListReadySessions + + "); do pm install-abandon $i; done"); + device.disableAdbRoot(); + } +} diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index aa3a13925894..b276f2ed4761 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -581,12 +581,18 @@ public final class ScanResult implements Parcelable { * 6 GHz band frequency of first channel in MHz * @hide */ - public static final int BAND_6_GHZ_START_FREQ_MHZ = 5945; + public static final int BAND_6_GHZ_START_FREQ_MHZ = 5955; /** * 6 GHz band frequency of last channel in MHz * @hide */ - public static final int BAND_6_GHZ_END_FREQ_MHZ = 7105; + public static final int BAND_6_GHZ_END_FREQ_MHZ = 7115; + + /** + * 6 GHz band operating class 136 channel 2 center frequency in MHz + * @hide + */ + public static final int BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ = 5935; /** * Utility function to check if a frequency within 2.4 GHz band @@ -618,7 +624,10 @@ public final class ScanResult implements Parcelable { * @hide */ public static boolean is6GHz(int freqMhz) { - return freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ; + if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) { + return true; + } + return (freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ); } /** @@ -649,6 +658,9 @@ public final class ScanResult implements Parcelable { } if (band == WifiScanner.WIFI_BAND_6_GHZ) { if (channel >= BAND_6_GHZ_FIRST_CH_NUM && channel <= BAND_6_GHZ_LAST_CH_NUM) { + if (channel == 2) { + return BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ; + } return ((channel - BAND_6_GHZ_FIRST_CH_NUM) * 5) + BAND_6_GHZ_START_FREQ_MHZ; } else { return UNSPECIFIED; @@ -673,6 +685,9 @@ public final class ScanResult implements Parcelable { } else if (is5GHz(freqMhz)) { return ((freqMhz - BAND_5_GHZ_START_FREQ_MHZ) / 5) + BAND_5_GHZ_FIRST_CH_NUM; } else if (is6GHz(freqMhz)) { + if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) { + return 2; + } return ((freqMhz - BAND_6_GHZ_START_FREQ_MHZ) / 5) + BAND_6_GHZ_FIRST_CH_NUM; } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 77fa673f1960..90edc4523b7b 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -30,6 +30,9 @@ import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1442,4 +1445,50 @@ public class WifiEnterpriseConfig implements Parcelable { } return TextUtils.isEmpty(getCaPath()); } + + /** + * Check if a given certificate Get the Suite-B cipher from the certificate + * + * @param x509Certificate Certificate to process + * @return true if the certificate OID matches the Suite-B requirements for RSA or ECDSA + * certificates, or false otherwise. + * @hide + */ + public static boolean isSuiteBCipherCert(@Nullable X509Certificate x509Certificate) { + if (x509Certificate == null) { + return false; + } + final String sigAlgOid = x509Certificate.getSigAlgOID(); + + // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates + // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192 + // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term + // Suite-B was already coined in the IEEE 802.11-2016 specification for + // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates + // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally + // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments, + // we are supporting both types here. + if (sigAlgOid.equals("1.2.840.113549.1.1.12")) { + // sha384WithRSAEncryption + if (x509Certificate.getPublicKey() instanceof RSAPublicKey) { + final RSAPublicKey rsaPublicKey = (RSAPublicKey) x509Certificate.getPublicKey(); + if (rsaPublicKey.getModulus() != null + && rsaPublicKey.getModulus().bitLength() >= 3072) { + return true; + } + } + } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) { + // ecdsa-with-SHA384 + if (x509Certificate.getPublicKey() instanceof ECPublicKey) { + final ECPublicKey ecPublicKey = (ECPublicKey) x509Certificate.getPublicKey(); + final ECParameterSpec ecParameterSpec = ecPublicKey.getParams(); + + if (ecParameterSpec != null && ecParameterSpec.getOrder() != null + && ecParameterSpec.getOrder().bitLength() >= 384) { + return true; + } + } + } + return false; + } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index b0213b0ef502..e12bb9178235 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -78,12 +78,12 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc private @Nullable String mWpa3SaePassphrase; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the WPA-EAP networks. + * certificates and other settings associated with the WPA/WPA2-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the SuiteB networks. + * certificates and other settings associated with the WPA3-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig; /** @@ -243,7 +243,11 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc /** * Set the associated enterprise configuration for this network. Needed for authenticating - * to WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description. + * to WPA3-Enterprise networks (standard and 192-bit security). See + * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the + * client and CA certificates must be provided, and must be of type of either + * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384 + * (OID 1.2.840.10045.4.3.3). * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link Builder} to enable chaining of the builder method. @@ -284,8 +288,25 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); configuration.enterpriseConfig = mWpa2EnterpriseConfig; - } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network - configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise + if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getClientCertificate()) + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getCaCertificate())) { + // WPA3-Enterprise in 192-bit security mode (Suite-B) + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else { + // WPA3-Enterprise + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); + configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + configuration.allowedPairwiseCiphers.set( + WifiConfiguration.PairwiseCipher.GCMP_256); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + configuration.requirePmf = true; + } configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 68eb1bbd8a79..d8be1d2c853c 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -72,12 +72,12 @@ public final class WifiNetworkSuggestion implements Parcelable { private @Nullable String mWpa3SaePassphrase; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the WPA-EAP networks. + * certificates and other settings associated with the WPA/WPA2-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the SuiteB networks. + * certificates and other settings associated with the WPA3-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig; /** @@ -276,7 +276,11 @@ public final class WifiNetworkSuggestion implements Parcelable { /** * Set the associated enterprise configuration for this network. Needed for authenticating - * to WPA3 enterprise networks. See {@link WifiEnterpriseConfig} for description. + * to WPA3-Enterprise networks (standard and 192-bit security). See + * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the + * client and CA certificates must be provided, and must be of type of either + * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384 + * (OID 1.2.840.10045.4.3.3). * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link Builder} to enable chaining of the builder method. @@ -522,8 +526,25 @@ public final class WifiNetworkSuggestion implements Parcelable { } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); configuration.enterpriseConfig = mWpa2EnterpriseConfig; - } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network - configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise + if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getClientCertificate()) + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getCaCertificate())) { + // WPA3-Enterprise in 192-bit security mode (Suite-B) + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else { + // WPA3-Enterprise + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); + configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + configuration.allowedPairwiseCiphers.set( + WifiConfiguration.PairwiseCipher.GCMP_256); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + configuration.requirePmf = true; + } configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); diff --git a/wifi/tests/src/android/net/wifi/FakeKeys.java b/wifi/tests/src/android/net/wifi/FakeKeys.java index 641b891a1f4d..c0d60c33f99c 100644 --- a/wifi/tests/src/android/net/wifi/FakeKeys.java +++ b/wifi/tests/src/android/net/wifi/FakeKeys.java @@ -214,35 +214,6 @@ public class FakeKeys { }; public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1); - private static final String CLIENT_SUITE_B_RSA3072_CERT_STRING = - "-----BEGIN CERTIFICATE-----\n" - + "MIIERzCCAq8CFDopjyNgaj+c2TN2k06h7okEWpHJMA0GCSqGSIb3DQEBDAUAMF4x\n" - + "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDTVRWMRAwDgYDVQQK\n" - + "DAdBbmRyb2lkMQ4wDAYDVQQLDAVXaS1GaTESMBAGA1UEAwwJdW5pdGVzdENBMB4X\n" - + "DTIwMDcyMTAyMjkxMVoXDTMwMDUzMDAyMjkxMVowYjELMAkGA1UEBhMCVVMxCzAJ\n" - + "BgNVBAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNVBAoMB0FuZHJvaWQxDjAMBgNV\n" - + "BAsMBVdpLUZpMRYwFAYDVQQDDA11bml0ZXN0Q2xpZW50MIIBojANBgkqhkiG9w0B\n" - + "AQEFAAOCAY8AMIIBigKCAYEAwSK3C5K5udtCKTnE14e8z2cZvwmB4Xe+a8+7QLud\n" - + "Hooc/lQzClgK4MbVUC0D3FE+U32C78SxKoTaRWtvPmNm+UaFT8KkwyUno/dv+2XD\n" - + "pd/zARQ+3FwAfWopAhEyCVSxwsCa+slQ4juRIMIuUC1Mm0NaptZyM3Tj/ICQEfpk\n" - + "o9qVIbiK6eoJMTkY8EWfAn7RTFdfR1OLuO0mVOjgLW9/+upYv6hZ19nAMAxw4QTJ\n" - + "x7lLwALX7B+tDYNEZHDqYL2zyvQWAj2HClere8QYILxkvktgBg2crEJJe4XbDH7L\n" - + "A3rrXmsiqf1ZbfFFEzK9NFqovL+qGh+zIP+588ShJFO9H/RDnDpiTnAFTWXQdTwg\n" - + "szSS0Vw2PB+JqEABAa9DeMvXT1Oy+NY3ItPHyy63nQZVI2rXANw4NhwS0Z6DF+Qs\n" - + "TNrj+GU7e4SG/EGR8SvldjYfQTWFLg1l/UT1hOOkQZwdsaW1zgKyeuiFB2KdMmbA\n" - + "Sq+Ux1L1KICo0IglwWcB/8nnAgMBAAEwDQYJKoZIhvcNAQEMBQADggGBAMYwJkNw\n" - + "BaCviKFmReDTMwWPRy4AMNViEeqAXgERwDEKwM7efjsaj5gctWfKsxX6UdLzkhgg\n" - + "6S/T6PxVWKzJ6l7SoOuTa6tMQOZp+h3R1mdfEQbw8B5cXBxZ+batzAai6Fiy1FKS\n" - + "/ka3INbcGfYuIYghfTrb4/NJKN06ZaQ1bpPwq0e4gN7800T2nbawvSf7r+8ZLcG3\n" - + "6bGCjRMwDSIipNvOwoj3TG315XC7TccX5difQ4sKOY+d2MkVJ3RiO0Ciw2ZbEW8d\n" - + "1FH5vUQJWnBUfSFznosGzLwH3iWfqlP+27jNE+qB2igEwCRFgVAouURx5ou43xuX\n" - + "qf6JkdI3HTJGLIWxkp7gOeln4dEaYzKjYw+P0VqJvKVqQ0IXiLjHgE0J9p0vgyD6\n" - + "HVVcP7U8RgqrbIjL1QgHU4KBhGi+WSUh/mRplUCNvHgcYdcHi/gHpj/j6ubwqIGV\n" - + "z4iSolAHYTmBWcLyE0NgpzE6ntp+53r2KaUJA99l2iGVzbWTwqPSm0XAVw==\n" - + "-----END CERTIFICATE-----\n"; - public static final X509Certificate CLIENT_SUITE_B_RSA3072_CERT = - loadCertificate(CLIENT_SUITE_B_RSA3072_CERT_STRING); - private static X509Certificate loadCertificate(String blob) { try { final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java index 5516f433070f..f1ec5e829316 100644 --- a/wifi/tests/src/android/net/wifi/ScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -102,9 +102,10 @@ public class ScanResultTest { 5845, WifiScanner.WIFI_BAND_5_GHZ, 169, 5865, WifiScanner.WIFI_BAND_5_GHZ, 173, /* Now some 6GHz channels */ - 5945, WifiScanner.WIFI_BAND_6_GHZ, 1, - 5960, WifiScanner.WIFI_BAND_6_GHZ, 4, - 6100, WifiScanner.WIFI_BAND_6_GHZ, 32 + 5955, WifiScanner.WIFI_BAND_6_GHZ, 1, + 5935, WifiScanner.WIFI_BAND_6_GHZ, 2, + 5970, WifiScanner.WIFI_BAND_6_GHZ, 4, + 6110, WifiScanner.WIFI_BAND_6_GHZ, 32 }; /** diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index d78c942d55e2..1a4427034756 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -282,6 +282,12 @@ public class SoftApConfigurationTest { .build(); assertNull(band_6g_config.toWifiConfiguration()); + SoftApConfiguration sae_transition_config = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .build(); + + assertNull(sae_transition_config.toWifiConfiguration()); } @Test @@ -324,16 +330,5 @@ public class SoftApConfigurationTest { assertThat(wifiConfig_2g5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_ANY); assertThat(wifiConfig_2g5g.apChannel).isEqualTo(0); assertThat(wifiConfig_2g5g.hiddenSSID).isEqualTo(true); - - SoftApConfiguration softApConfig_sae_transition = new SoftApConfiguration.Builder() - .setPassphrase("secretsecret", - SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) - .build(); - - WifiConfiguration wifiConfig_sae_transition = - softApConfig_sae_transition.toWifiConfiguration(); - assertThat(wifiConfig_sae_transition.getAuthType()) - .isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK); - assertThat(wifiConfig_sae_transition.preSharedKey).isEqualTo("secretsecret"); } } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index 8270d643ca65..638efb9f14ee 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -23,8 +23,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import android.net.wifi.EAPConstants; -import android.net.wifi.FakeKeys; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Parcel; @@ -34,11 +32,6 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -390,39 +383,19 @@ public class PasspointConfigurationTest { } /** - * Verify that the unique identifier generated is the same for two instances with different - * HomeSp node but same FQDN + * Verify that the unique identifier generated is different for two instances with different + * HomeSp node * * @throws Exception */ @Test - public void validateUniqueIdDifferentHomeSpSameFqdn() throws Exception { + public void validateUniqueIdDifferentHomeSp() throws Exception { PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - // Modify config2's RCOIs and friendly name to a different set of values + // Modify config2's RCOIs to a different set of values PasspointConfiguration config2 = PasspointTestUtils.createConfig(); HomeSp homeSp = config2.getHomeSp(); homeSp.setRoamingConsortiumOis(new long[] {0xaa, 0xbb}); - homeSp.setFriendlyName("Some other name"); - config2.setHomeSp(homeSp); - - assertEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is different for two instances with the same - * HomeSp node but different FQDN - * - * @throws Exception - */ - @Test - public void validateUniqueIdSameHomeSpDifferentFqdn() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - - // Modify config2's FQDN to a different value - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - HomeSp homeSp = config2.getHomeSp(); - homeSp.setFqdn("fqdn2.com"); config2.setHomeSp(homeSp); assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); @@ -430,15 +403,15 @@ public class PasspointConfigurationTest { /** * Verify that the unique identifier generated is different for two instances with different - * SIM Credential node + * Credential node * * @throws Exception */ @Test - public void validateUniqueIdDifferentSimCredential() throws Exception { + public void validateUniqueIdDifferentCredential() throws Exception { PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - // Modify config2's realm and SIM credential to a different set of values + // Modify config2's RCOIs to a different set of values PasspointConfiguration config2 = PasspointTestUtils.createConfig(); Credential credential = config2.getCredential(); credential.setRealm("realm2.example.com"); @@ -449,157 +422,6 @@ public class PasspointConfigurationTest { } /** - * Verify that the unique identifier generated is different for two instances with different - * Realm in the Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdDifferentRealm() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - - // Modify config2's realm to a different set of values - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - Credential credential = config2.getCredential(); - credential.setRealm("realm2.example.com"); - config2.setCredential(credential); - - assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is the same for two instances with different - * password and same username in the User Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdSameUserInUserCredential() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - Credential credential = createCredentialWithUserCredential("user", "passwd"); - config1.setCredential(credential); - - // Modify config2's Passpowrd to a different set of values - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - credential = createCredentialWithUserCredential("user", "newpasswd"); - config2.setCredential(credential); - - assertEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is different for two instances with different - * username in the User Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdDifferentUserCredential() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - Credential credential = createCredentialWithUserCredential("user", "passwd"); - config1.setCredential(credential); - - // Modify config2's username to a different value - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - credential = createCredentialWithUserCredential("user2", "passwd"); - config2.setCredential(credential); - - assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is different for two instances with different - * Cert Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdDifferentCertCredential() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - Credential credential = createCredentialWithCertificateCredential(true, true); - config1.setCredential(credential); - - // Modify config2's cert credential to a different set of values - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - credential = createCredentialWithCertificateCredential(false, false); - config2.setCredential(credential); - - assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Helper function for generating certificate credential for testing. - * - * @return {@link Credential} - */ - private static Credential createCredentialWithCertificateCredential(Boolean useCaCert0, - Boolean useCert0) - throws NoSuchAlgorithmException, CertificateEncodingException { - Credential.CertificateCredential certCred = new Credential.CertificateCredential(); - certCred.setCertType("x509v3"); - if (useCert0) { - certCred.setCertSha256Fingerprint( - MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded())); - } else { - certCred.setCertSha256Fingerprint(MessageDigest.getInstance("SHA-256") - .digest(FakeKeys.CLIENT_SUITE_B_RSA3072_CERT.getEncoded())); - } - return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT}, - FakeKeys.RSA_KEY1, useCaCert0 ? FakeKeys.CA_CERT0 : FakeKeys.CA_CERT1); - } - - /** - * Helper function for generating user credential for testing. - * - * @return {@link Credential} - */ - private static Credential createCredentialWithUserCredential(String username, String password) { - Credential.UserCredential userCred = new Credential.UserCredential(); - userCred.setUsername(username); - userCred.setPassword(password); - userCred.setMachineManaged(true); - userCred.setAbleToShare(true); - userCred.setSoftTokenApp("TestApp"); - userCred.setEapType(EAPConstants.EAP_TTLS); - userCred.setNonEapInnerMethod("MS-CHAP"); - return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0); - } - - /** - * Helper function for generating Credential for testing. - * - * @param userCred Instance of UserCredential - * @param certCred Instance of CertificateCredential - * @param simCred Instance of SimCredential - * @param clientCertificateChain Chain of client certificates - * @param clientPrivateKey Client private key - * @param caCerts CA certificates - * @return {@link Credential} - */ - private static Credential createCredential(Credential.UserCredential userCred, - Credential.CertificateCredential certCred, - Credential.SimCredential simCred, - X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey, - X509Certificate... caCerts) { - Credential cred = new Credential(); - cred.setCreationTimeInMillis(123455L); - cred.setExpirationTimeInMillis(2310093L); - cred.setRealm("realm"); - cred.setCheckAaaServerCertStatus(true); - cred.setUserCredential(userCred); - cred.setCertCredential(certCred); - cred.setSimCredential(simCred); - if (caCerts != null && caCerts.length == 1) { - cred.setCaCertificate(caCerts[0]); - } else { - cred.setCaCertificates(caCerts); - } - cred.setClientCertificateChain(clientCertificateChain); - cred.setClientPrivateKey(clientPrivateKey); - return cred; - } - - /** * Verify that the unique identifier API generates an exception if HomeSP is not initialized. * * @throws Exception diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index a44df40a8e97..829d8f0a9a3a 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -593,10 +593,10 @@ public class CredentialTest { } /** - * Verify that unique identifiers are different for a credential with different username + * Verify that unique identifiers are different for a credential with different values */ @Test - public void testUniqueIdDifferentForUserCredentialsWithDifferentUsername() throws Exception { + public void testUniqueIdDifferentForUserCredentialsWithDifferentValues() throws Exception { Credential userCred1 = createCredentialWithUserCredential(); Credential userCred2 = createCredentialWithUserCredential(); userCred2.getUserCredential().setUsername("anotheruser"); @@ -605,24 +605,7 @@ public class CredentialTest { } /** - * Verify that unique identifiers are different for a credential with different password and - * other values other than username - */ - @Test - public void testUniqueIdSameForUserCredentialsWithDifferentPassword() throws Exception { - Credential userCred1 = createCredentialWithUserCredential(); - Credential userCred2 = createCredentialWithUserCredential(); - userCred2.getUserCredential().setPassword("someotherpassword!"); - userCred2.getUserCredential().setMachineManaged(false); - userCred2.getUserCredential().setAbleToShare(false); - userCred2.getUserCredential().setSoftTokenApp("TestApp2"); - userCred2.getUserCredential().setNonEapInnerMethod("PAP"); - - assertEquals(userCred1.getUniqueId(), userCred2.getUniqueId()); - } - - /** - * Verify that unique identifiers are different for a cert credential with different values + * Verify that unique identifiers are different for a credential with different values */ @Test public void testUniqueIdDifferentForCertCredentialsWithDifferentValues() throws Exception { |